/*
 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
 * 2012, 2013, 2014 Robert Lougher <rob@jamvm.org.uk>.
 *
 * This file is part of JamVM.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <stddef.h>

#include "jam.h"
#include "sig.h"
#include "thread.h"
#include "lock.h"
#include "hash.h"
#include "zip.h"
#include "class.h"
#include "interp.h"
#include "symbol.h"
#include "excep.h"
#include "classlib.h"

#define PREPARE(ptr) ptr
#define SCAVENGE(ptr) FALSE
#define FOUND(ptr1, ptr2) ptr2

static int verbose;
static char *bootpath;
static char *classpath;
static int max_cp_element_len;

/* Structures holding the boot loader classpath 保存引导加载程序类路径的结构 */
typedef struct bcp_entry {
    char *path;
    ZipFile *zip;
} BCPEntry;

static BCPEntry *bootclasspath;
static int bcp_entries;

/* Cached offsets of fields in java.lang.ref.Reference objects */
int ref_referent_offset = -1;
int ref_queue_offset;

/* hash table containing packages loaded by the boot loader 
    包含由引导加载程序加载的包的哈希表 */
#define PCKG_INITSZE 1<<6
static HashTable boot_packages;

/* Instance of java.lang.Class for java.lang.Class 
    java.lang.Class的java.lang.Class实例 */
Class *java_lang_Class = NULL;

/* Method table index of ClassLoader.loadClass - used when
   requesting a Java-level class loader to load a class.
   Cached on first use. */
static int loadClass_mtbl_idx = -1;

/* Method table index of finalizer method and ClassLoader.enqueue.
   Used by finalizer and reference handler threads */
int finalize_mtbl_idx;
int enqueue_mtbl_idx;

/* hash table containing classes loaded by the boot loader and
   internally created arrays 
   包含由引导加载程序加载的类和内部创建的数组的散列表 */
#define CLASS_INITSZE 1<<8
static HashTable boot_classes;

/* Array large enough to hold all primitive classes -
 * access protected by boot_classes hash table lock 数组足够大，以保存所有原语类-访问受boot_classes哈希表锁保护 */
#define MAX_PRIM_CLASSES 9
static Class *prim_classes[MAX_PRIM_CLASSES];

/* Bytecode for stub abstract method.  If it is invoked
   we'll get an abstract method error. 
    存根抽象方法的字节码。如果它被调用，我们将得到一个抽象方法错误。 */
static char abstract_method[] = {OPC_ABSTRACT_METHOD_ERROR};

/* Bytecode for a Miranda method.  If it is invoked it executes
   the unimplemented interface method that it represents. 
   Miranda方法的字节码。如果它被调用，它将执行它所表示的未实现的接口方法。 */
static char miranda_bridge[] = {OPC_MIRANDA_BRIDGE};

static Class *addClassToHash(Class *class, Object *class_loader) {
    HashTable *table;
    Class *entry;

#define HASH(ptr) utf8Hash(CLASS_CB((Class *)ptr)->name)
#define COMPARE(ptr1, ptr2, hash1, hash2) (hash1 == hash2) && \
            CLASS_CB((Class *)ptr1)->name == CLASS_CB((Class *)ptr2)->name

    if(class_loader == NULL)
        table = &boot_classes;
    else {
        table = classlibLoaderTable(class_loader);

        if(table == NULL) {
            table = classlibCreateLoaderTable(class_loader);

            if(table == NULL)
                return NULL;
        }
    }

    /* Add if absent, no scavenge, locked */
    findHashEntry((*table), class, entry, TRUE, FALSE, TRUE);

    return entry;
}
/*
   JVM定义为：
    准备阶段是正式为类中定义的变量(即静态变量，被static修饰的变量)分配内存并设置类变量初 始值的阶段。

    但是这个咋这么简单...就没了
*/
static void prepareClass(Class *class) {
    ClassBlock *cb = CLASS_CB(class);

    if(cb->name == SYMBOL(java_lang_Class)) {
       // 是java.lang.Class实例
       java_lang_Class = class->class = class;
       cb->flags |= CLASS_CLASS;
    } else {
        // TODO 这里为啥一定要把Class的class属性设置为java.lang.Class？
       if(java_lang_Class == NULL)
          findSystemClass0(SYMBOL(java_lang_Class));
       class->class = java_lang_Class;
    }
}

#ifdef CLASSLIB_METHOD_ANNOTATIONS
/* Forward declaration */
u1 *skipAnnotation(u1 *data_ptr, int *data_len);

u1 *skipElementValue(u1 *data_ptr, int *data_len) {
    char tag;

    READ_U1(tag, data_ptr, *data_len);

    switch(tag) {
        case 'e':
            SKIP_U2(idx, data_ptr, *data_len);
            /* Fall through */

        case 'Z': case 'B': case 'C': case 'S': case 'I':
        case 'F': case 'J': case 'D': case 's': case 'c':
            SKIP_U2(idx, data_ptr, *data_len);
            break;

        case '@':
            data_ptr = skipAnnotation(data_ptr, data_len);
            break;

        case '[': {
            int i, num_values;

            READ_U2(num_values, data_ptr, *data_len);

            for(i = 0; i < num_values; i++)
                data_ptr = skipElementValue(data_ptr, data_len);

            break;
        }
    }

    return data_ptr;
}

u1 *skipAnnotation(u1 *data_ptr, int *data_len) {
    int no_value_pairs, i;

    SKIP_U2(type_idx, data_ptr, *data_len);
    READ_U2(no_value_pairs, data_ptr, *data_len);

    for(i = 0; i < no_value_pairs; i++) {
        SKIP_U2(element_name_idx, data_ptr, *data_len);
        data_ptr = skipElementValue(data_ptr, data_len);
    }

    return data_ptr;
}

void parseMethodAnnotations(ConstantPool *cp, MethodBlock *mb,
                            u1 *data_ptr, int data_len) {
    int no_annos, i;

    READ_U2(no_annos, data_ptr, data_len);

    for(i = 0; i < no_annos; i++) {
        u1 *ptr = data_ptr;
        char *type_name;
        int type_idx;

        data_ptr = skipAnnotation(data_ptr, &data_len);

        READ_TYPE_INDEX(type_idx, cp, CONSTANT_Utf8, ptr, 2);
        type_name = findUtf8(CP_UTF8(cp, type_idx));

        if(type_name != NULL)
            CLASSLIB_METHOD_ANNOTATIONS(mb, type_name);
    }
}
#else
void parseMethodAnnotations(ConstantPool *cp, MethodBlock *mb,
                            u1 *data_ptr, int data_len) {
}
#endif

static void setIndexedAttributeData(AttributeData ***attributes,
                                    int index, u1 *data, int len,
                                    int size) {
    if(*attributes == NULL) {
        *attributes = sysMalloc(size * sizeof(AttributeData*));
        memset(*attributes, 0, size * sizeof(AttributeData*));
    }

    (*attributes)[index] = sysMalloc(sizeof(AttributeData));
    (*attributes)[index]->len = len;
    (*attributes)[index]->data = sysMalloc(len);
    memcpy((*attributes)[index]->data, data, len);
}

#define setIndexedAttribute(attributes, index, data, len, size) \
    setIndexedAttributeData(&attributes, index, data, len, size)

#define setSingleAttribute(attributes, pntr, length) \
    attributes = sysMalloc(sizeof(AttributeData));   \
    attributes->len = length;                        \
    attributes->data = sysMalloc(length);            \
    memcpy(attributes->data, pntr, length);

/*
    这看名字就是解析一个Class 从data中获取数据然后解析到ClassBlock中，转换成Class对象进行返回
    这个步骤属于Class加载当中的 加载（将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构）
    和验证（一部分字节码文件格式验证动作）交叉进行的方法封装
    // 还有解析（解析阶段是Java虚拟机将常量池内的符号引用替换为直接引用的过程）。 这个划掉 这里应该不是 这里还是符号引用
    所以，这个流程不像是JVM所表述的那样，加载、链接（验证、准备、解析）、初始化、使用、卸载，完全独立的
    至少加载和链接这块，有一些是重叠了
*/
Class *parseClass(char *classname, char *data, int offset, int len,
                   Object *class_loader) {

    u2 major_version, minor_version, this_idx, super_idx, attr_count; //u2: unsigned short, major_version: 主要版本, minor_version 小版本, 
    int cp_count, intf_count, injected_fields_count, i, j;
    ExtraAttributes extra_attributes;
    u1 *ptr = (u1 *)data + offset;
    ConstantPool *constant_pool;
    Class **interfaces, *class;
    ClassBlock *classblock;
    u4 magic; // 4个字节

    READ_U4(magic, ptr, len); // 读取4个字节

    // 每个Class文件的头4个字节被称为魔数(M agic Number)，它的唯一作用是确定这个文件是否为 一个能被虚拟机接受的Class文件。
    if(magic != 0xcafebabe) { // class文件都是从 0xcafebabe 这个字符串开头的 这个打开一个class以二进制来头就可以看到
       signalException(java_lang_ClassFormatError, "bad magic");
       return NULL;
    }

    /* 紧接着魔数的4个字节存储的是Class文件的版本号:第5和第6个字节是次版本号(Minor Version)，第7和第8个字节是主版本号(Major Version)。
        Java的版本号是从45开始的，JDK 1.1之后 的每个JDK大版本发布主版本号向上加1(JDK 1.0~1.1使用了45.0~45.3的版本号)，
        高版本的JDK能 向下兼容以前版本的Class文件，但不能运行以后版本的Class文件，
        因为《Java虚拟机规范》在Class文 件校验部分明确要求了即使文件格式并未发生任何变化，虚拟机也必须拒绝执行超过其版本号的Class 文件。*/
    READ_U2(minor_version, ptr, len);  // 读取两个字节 就是次版本
    READ_U2(major_version, ptr, len);  // 读取2个字节 就是大版本

    // TODO 突然发现一个事情 这个分配Class内存方里，好像并没有指定是哪一块内存区域啊？
    // 是不是jamvm不支持把内存区域进行划分啊？
    if((class = allocClass()) == NULL)  // 分配内存
        return NULL;

    // 把class转换成classblock 用做数据封装
    classblock = CLASS_CB(class);
    // 接下来的两个字节 为常量池的数量
    /* 这个容量计数是从1而不是0开始 
        设计者将第0项常量 空出来是有特殊考虑的，这样做的目的在于，
        如果后面某些指向常量池的索引值的数据在特定情况下 需要表达“不引用任何一个常量池项目”的含义，可以把索引值设置为0来表示。*/
    READ_U2(cp_count, ptr, len);

    /* 常量池中每一项常量都是一个表，最初常量表中共有11种结构各不相同的表结构数据，后来为了
        更好地支持动态语言调用，额外增加了4种动态语言相关的常量[1]，
        为了支持Java模块化系统 (Jigsaw)，又加入了CONSTANT_M odule_info和CONSTANT_Package_info两个常量，
        所以截至JDK 13，常量表中分别有17种不同类型的常量。
        */
    constant_pool = &classblock->constant_pool;  // 常量池？感觉这些东西需要看一下Class文件对象的博客了 不然有点懵逼
    constant_pool->type = sysMalloc(cp_count);
    constant_pool->info = sysMalloc(cp_count * sizeof(ConstantPoolEntry)); // 应该是给常量池分配内存

    for(i = 1; i < cp_count; i++) {
        u1 tag;// 这17类表都有一个共同的特点，表结构起始的第一位是个u1类型的标志位(tag)，代表着当前常量属于哪种常量类型。

        READ_U1(tag, ptr, len);
        CP_TYPE(constant_pool, i) = tag;

        // 在jamvm中支持14中tag解析
        switch(tag) {  
           case CONSTANT_Class: // 类或接口的符号引用
           /*
            类型    名称            数量        描述
            u1      tag             1         值为7
            u2      name_index      1       指向全限定名常量项的索引

            tag是标志位，它用于区分常量类型;
            name_index是常量池的索引值，它指向常量池中一个 CONSTANT_Utf8_info类型常量
            顺便提一下，由于Class文件中方法、字段等都需要引用CONSTANT_Utf8_info型常量来描述名称，
            所以CONSTANT_Utf8_info型常量的最大长度也就是Java中方法、字段名的最大长度。
            而这里的最大长度就是lengt h的最大值(查看下面的case即可知)，
                既u2类型能表达的最大值65535。所以J ava程序中如果定义了超过64KB英文字符的变量或方法名，即使规则和全部字符都是合法的，也会无法编译。*/
           case CONSTANT_String: // 字符串类型字面常量
                /*
                类型    名称            数量        描述
                u1      tag             1          值为8
                u2      index           1       指向字符串字面常量的索引
                */
           case CONSTANT_MethodType: // 方法类型
                /*
                类型    名称            数量        描述
                u1      tag             1          值为16
                u2      descriptor_kind   1       值必须是对常量池的有效索引，常量池在该索引处的项必须是CONSTANT_Utf8结构，标识方法的描述符*/
               READ_INDEX(CP_INFO(constant_pool, i), ptr, len); // 从ptr中读取两个字节到constant_pool->info[i]中
               break;

           case CONSTANT_Fieldref: // 字段的符号引用
                /*
                类型    名称            数量        描述
                u1      tag             1          值为9
                u2      index           1       指向声明字段的类或者接口描述符CONSTANT_Class的索引项
                u2      index           1       指向字段描述符CONSTANT_NameAndType的索引项
                */
           case CONSTANT_Methodref: // 类中方法的符号引用
                /*
                类型    名称            数量        描述
                u1      tag             1          值为10
                u2      index           1       指向声明方法的类描述符CONSTANT_Class的索引项
                u2      index           1       指向名称及类型描述符CONSTANT_NameAndType的索引项
                */
           case CONSTANT_NameAndType: // 字段或者方法的部分符号引用
                /*
                类型    名称            数量        描述
                u1      tag             1          值为12
                u2      index           1       指向该字段或方法名称常量项的索引
                u2      index           1       指向该字段或方法描述符常量项的索引
                */
           case CONSTANT_InvokeDynamic: // 表示一个动态方法调用点
                 /*
                类型    名称                     数量        描述
                u1      tag                      1          值为18
                u2 bootstartp_method_attr_index  1       值必须是对当前Class文件中引导方法表的bootstrap_method[]数组的有效索引
                u2 name_and_type_index          1       值必须是对当前常量池的有效索引，常量池在改索引出的项必须是CONSTANT_NameAndType结构，标识方法名和方法描述符
                
                */
           case CONSTANT_InterfaceMethodref: // 接口中方法的符号引用
            /*
                类型    名称            数量        描述
                u1      tag             1          值为11
                u2      index           1       指向声明方法的接口描述符CONSTANT_Class的索引项
                u2      index           1       指向名称及类型描述符CONSTANT_NameAndType的索引项
                */
           {
               u2 idx1, idx2;

               READ_INDEX(idx1, ptr, len);
               READ_INDEX(idx2, ptr, len); // 这里需要读两次 
               CP_INFO(constant_pool, i) = (idx2<<16) + idx1; // 后2个字节是高位 + 低位组成了一个数据
               break;
           }

           case CONSTANT_MethodHandle: // 方法句柄
           /*
                类型    名称            数量        描述
                u1      tag             1          值为15
                u1      refrence_kind   1       值必须在1~9之间（包含1和9），它决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为
                u2      refrence_index  1       值必须是对常量池的有效索引
                */
           {
               u1 kind;
               u2 idx;

               READ_U1(kind, ptr, len); // 这里读的一个字节
               READ_INDEX(idx, ptr, len); // 然后读两个字节 同上 读两次才能组成一个合法的数据
               CP_INFO(constant_pool, i) = (idx<<16) + kind; 
               break;
           }

           case CONSTANT_Float: // 浮点型字面常量
                /*
                类型    名称            数量        描述
                u1      tag             1          值为4
                u4      bytes           1       按照高位在前存储的float值
                */
           case CONSTANT_Integer: // 整形字面常量
               /*
                类型    名称            数量        描述
                u1      tag             1          值为3
                u4      bytes           1       按照高位在前存储的int值
                */
               READ_U4(CP_INFO(constant_pool, i), ptr, len); // 直接读4个字节 因为是常量嘛
               break;

           case CONSTANT_Long: // 长整型字面常量
               /*
                类型    名称            数量        描述
                u1      tag             1          值为5
                u8      bytes           1       按照高位在前存储的long值
                */
               READ_U8(*(u8 *)&(CP_INFO(constant_pool, i)), ptr, len); // 读8个字节
               CP_TYPE(constant_pool, ++i) = 0; // TODO 这里我猜测是因为constant_pool->info是long int, 4个字节不够用，所以占了两个表 然后把下个部分的type置为0 表示这部分数据是上个数据的一部分？
               break;
               
           case CONSTANT_Double: // 双精度浮点型字面常量
               /*
                类型    名称            数量        描述
                u1      tag             1          值为6
                u8      bytes           1       按照高位在前存储的double值
                */
               READ_DBL(*(u8 *)&(CP_INFO(constant_pool, i)), ptr, len);
               CP_TYPE(constant_pool, ++i) = 0;
               break;

           case CONSTANT_Utf8: // UTF-8编码的字符串
           {
               /*
                类型    名称            数量
                u1      tag             1
                u2      length          1
                u1      bytes           length
               */
               int length;
               char *buff, *utf8;

               READ_U2(length, ptr, len);
               buff = sysMalloc(length + 1);

               memcpy(buff, ptr, length); // copy length长度的数据从ptr到buff中
               buff[length] = '\0';
               ptr += length;

               CP_INFO(constant_pool, i) = (uintptr_t) (utf8 = newUtf8(buff));

               if(utf8 != buff)
                   sysFree(buff);

               break;
           }

           default:
               signalException(java_lang_ClassFormatError,
                               "bad constant pool tag");
               return NULL;
        }
    }

    /* Set count after constant pool has been initialised -- it is now
       safe to be scanned by GC 在常量池初始化后设置count——现在GC可以安全地扫描它了 */
    classblock->constant_pool_count = cp_count; // TODO 这个和GC有啥关系？

    /* 在常量池结束之后，紧接着的2个字节代表访问标志(access_flags)，
        这个标志用于识别一些类或 者接口层次的访问信息，
        包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract 类型;
        如果是类的话，是否被声明为final;
        如果是一个public的类除此之外没有别的修饰符的话，应该是0021*/
    READ_U2(classblock->access_flags, ptr, len); // 读两个字节

    // 第一个u2的数据为 类索引 this_index or this_class
    READ_TYPE_INDEX(this_idx, constant_pool, CONSTANT_Class, ptr, len); // u2类型数据
    // 这里应该是需要两次定位的，第一次定位是定位class 然后第二次定位是把class对应的具体字符串给定位出来
    // 参考HelloWorld.txt的数据
    // #5 = Class              #21            //  HelloWorld
    // 比如上面这个 this_index肯定是5 得到Class 但是这个Class又指向21 也就是HelloWorld这个字符串
    classblock->name = CP_UTF8(constant_pool,
                               CP_CLASS(constant_pool, this_idx));

    // 判断Class名称与要解析的Class名称是否一致
    if(classname && strcmp(classblock->name, classname) != 0) {
        signalException(java_lang_NoClassDefFoundError,
                        "class file has wrong name");
        return NULL;
    }

    // TODO 准备Class
    prepareClass(class);
    // 接下来的u2数据为父类索引 super_class
    if(classblock->name == SYMBOL(java_lang_Object)) {
        // 如果当前类是java_lang_Object 再读取u2字节数据
        READ_U2(super_idx, ptr, len);
        if(super_idx) {
            // 还有数据那就有问题了，Object不允许有父类的！
           signalException(java_lang_ClassFormatError, "Object has super");
           return NULL;
        }
    } else {
        // 否则的话，就是读2个字节咯 
        READ_TYPE_INDEX(super_idx, constant_pool, CONSTANT_Class, ptr, len);
    }

    // 类加载器
    classblock->class_loader = class_loader;

    // 接下来的u2数据为接口索引的数量
    READ_U2(intf_count = classblock->interfaces_count, ptr, len);
    // 分配内存
    interfaces = classblock->interfaces =
                         sysMalloc(intf_count * sizeof(Class *));
    // 初始哈
    memset(interfaces, 0, intf_count * sizeof(Class *));
    // 根据索引数量进行循环
    for(i = 0; i < intf_count; i++) {
       u2 index;
       // 每次读u2长度的数据 就是当前类实现的接口的数据
       READ_TYPE_INDEX(index, constant_pool, CONSTANT_Class, ptr, len);
       // TODO 解析Class
       interfaces[i] = resolveClass(class, index, FALSE, FALSE);
       // 出错了就终止
       if(exceptionOccurred())
           return NULL; 
    }

    // 初始化把数据都搞为0
    memset(&extra_attributes, 0, sizeof(ExtraAttributes));
    

    /* 下面一部分就是字段数据的相关解析了*/

    /*
        字段结构表
        类型            名称                    数量
        u2           access_flags               1
        u2            name_index                1
        u2          descriptior_index           1
        u2          attrbutes_count             1
    attribute_info      attributes          attrbutes_count
    字段修饰符放在access_flags项目中，它与类中的access_flags项目是非常类似的，都是一个u2的数据类型
    */
   // 读取字段数量
    READ_U2(classblock->fields_count, ptr, len);
    // TODO 注入的字段的数量？ 这个用来干嘛
    injected_fields_count = classlibInjectedFieldsCount(classblock->name);
    // 加上注入的数量，重新计算字段数量
    classblock->fields_count += injected_fields_count;
    classblock->fields = sysMalloc(classblock->fields_count *
                                   sizeof(FieldBlock));

    if(injected_fields_count != 0)
        // TODO 填充自动注入的字段
        // 看起来 这个做法是某些虚拟机独有的，运行时需要的一些自动生成的字段？ 比如内部类的this指向？我猜的
        classlibFillInInjectedFields(classblock->name, classblock->fields);

    // 有了字段数量之后 就是循环获取每一个字段的详细信息
    for(i = injected_fields_count; i < classblock->fields_count; i++) {
        FieldBlock *field = &classblock->fields[i];
        u2 name_idx, type_idx;
        // 每一个字段信息的前2个字节是access_flag
        READ_U2(field->access_flags, ptr, len);
        // 随后是字段的简单名称的常量池引用  比如一个字段 private static Long longVal; 则这个字段的简单名称为 longVal
        READ_TYPE_INDEX(name_idx, constant_pool, CONSTANT_Utf8, ptr, len);
        // 简单名称之后是字段的描述符（深入理解JVM的书里用descriptor_index表示 这里是type_idx）
        // 对于字段 private static Long longVal;来说这个字段的描述符为Ljava/lang/Long; 表示一个对象类型Long
        READ_TYPE_INDEX(type_idx, constant_pool, CONSTANT_Utf8, ptr, len);
        // 然后捏 就是根据常量池引用 去获取对应的实际数据
        field->name = CP_UTF8(constant_pool, name_idx);
        field->type = CP_UTF8(constant_pool, type_idx);
        field->signature = NULL;  // 默认为空 如果有设置 则下面会赋值
        field->constant = 0; // 默认为0 如果有设置 则下面会赋值

        for(j = 0; j < injected_fields_count; j++)
            // 这里应该就是避免自动注入的字段和Class中已经定义的字段名称重复的判断
            if(field->name == classblock->fields[j].name) {
                jam_fprintf(stderr, "Classlib mismatch: injected field "
                                    "\"%s\" already present in %s\n",
                                    field->name, classblock->name);
                exitVM(1);
            } 
        // 属性表集合，用于存储一些额外的信息 
        // 比如一个字段private static Long longVal = 10 ; 那就可能会存在一项名称为ConstantValue的属性，其值指向常量10。
        // TODO 关于attribute_info的更多内容 可以查看6.3.7节介绍属性表的数据项目
        READ_U2(attr_count, ptr, len);
        for(; attr_count != 0; attr_count--) {
            u2 attr_name_idx;
            char *attr_name;
            u4 attr_length;

            READ_TYPE_INDEX(attr_name_idx, constant_pool, CONSTANT_Utf8,
                            ptr, len);
            attr_name = CP_UTF8(constant_pool, attr_name_idx);
            READ_U4(attr_length, ptr, len);

            if(attr_name == SYMBOL(ConstantValue)) {
                // 常量
                READ_INDEX(classblock->fields[i].constant, ptr, len);

            } else if(attr_name == SYMBOL(Signature)) {
                // TODO 这个是啥
                u2 signature_idx;
                READ_TYPE_INDEX(signature_idx, constant_pool, CONSTANT_Utf8,
                                ptr, len);
                field->signature = CP_UTF8(constant_pool, signature_idx);

            } else if(attr_name == SYMBOL(RuntimeVisibleAnnotations)) {
                // TODO 这个是啥
                setIndexedAttribute(extra_attributes.field_annos,
                                    field - classblock->fields, ptr,
                                    attr_length, classblock->fields_count); 
                ptr += attr_length;
#ifdef JSR308
            } else if(attr_name == SYMBOL(RuntimeVisibleTypeAnnotations)) {
                setIndexedAttribute(extra_attributes.field_type_annos,
                                    field - classblock->fields, ptr,
                                    attr_length, classblock->fields_count); 
                ptr += attr_length;
#endif
            } else
                ptr += attr_length;
        }
    }
    /* 方法表的结构如同字段表一样，依 次包括访问标志(access_flags)、名称索引(name_index)、描述符索引(descrip tor_index)、属性表 集合(attributes)几项 
        在访问标 志和属性表集合的可选项中有所区别
        因为volat ile关键字和t rans ient 关键字不能修饰方法，所以方法表的访问标志中没有了 ACC_VOLATILE标志和ACC_TRANSIENT标志。
        与之相对，synchronized、native、strictfp和abstract 关键字可以修饰方法，
        方法表的访问标志中也相应地增加了ACC_SYNCHRONIZED、 ACC_NATIVE、ACC_STRICTFP和ACC_ABSTRACT标志。
        */
     /*
        字段结构表
        类型            名称                    数量
        u2           access_flags               1
        u2            name_index                1
        u2          descriptior_index           1
        u2          attrbutes_count             1
    attribute_info      attributes          attrbutes_count
    */
    // 先是方法数量
    READ_U2(classblock->methods_count, ptr, len);
    // 分配内存
    classblock->methods = sysMalloc(classblock->methods_count *
                                    sizeof(MethodBlock));
    // 初始化
    memset(classblock->methods, 0, classblock->methods_count *
                                   sizeof(MethodBlock));
    // 根据方法数量循环获取方法信息
    for(i = 0; i < classblock->methods_count; i++) {
        MethodBlock *method = &classblock->methods[i];
        u2 name_idx, type_idx;
        // 读取访问标志
        READ_U2(method->access_flags, ptr, len);
        // 读取简单名称的常量池引用
        READ_TYPE_INDEX(name_idx, constant_pool, CONSTANT_Utf8, ptr, len);
        // 读取描述符常量池引用
        READ_TYPE_INDEX(type_idx, constant_pool, CONSTANT_Utf8, ptr, len);
        // 从常量池获取对应的真实数据
        method->name = CP_UTF8(constant_pool, name_idx);
        method->type = CP_UTF8(constant_pool, type_idx);

        // TODO 思考一下  从JVM层面为啥Java中不能出现返回值不同的重载？？

        // 属性数量 存储着诸如 代码、异常注解等等信息的表 具体可以查看《深入理解JVM》的表6-13
        READ_U2(attr_count, ptr, len);
        for(; attr_count != 0; attr_count--) {
            /* 对于每一个属性，它的名称都要从常量池中引用一个CONSTANT_Utf8_info类型的常量来表示， 
                而属性值的结构则是完全自定义的，只需要通过一个u4的长度属性去说明属性值所占用的位数即可。*/
            /* 
                类型            名称                    数量
                 u2           attribute_name_index      1
                 u4          attribute_length           1
                 u1          info                   attribute_length
        */
            u2 attr_name_idx; // attribute_name_index是一项指向CONSTANT_Utf8_info型常量的索引，此常量值固定为“Code”，它 代表了该属性的属性名称
            char *attr_name;
            u4 attr_length; // at t ribut e_length指示了属性值的长度，由于属性名称索引与属性长度一共为 6个字节，所以属性值的长度固定为整个属性表长度减去6个字节。

            READ_TYPE_INDEX(attr_name_idx, constant_pool, CONSTANT_Utf8,
                            ptr, len);
            READ_U4(attr_length, ptr, len);
            attr_name = CP_UTF8(constant_pool, attr_name_idx);

            if(attr_name == SYMBOL(Code)) { // 方法表 Java代码编译成的字节码指令
                /* 方法里的Java代码，经过Javac编译器编译成字节码指令之后，存放在方法属性表集合中一个名为“Code”的属性里面 
                    接口或者抽 象类中的方法就不存在Code属性*/
                u4 code_length;
                u2 code_attr_cnt;
                int j;

                // max_stack代表了操作数栈(Operand Stack)深度的最大值，在方法执行的任意时刻，操作数栈都 不会超过这个深度。虚拟机运行的时候需要根据这个值来分配栈帧(Stack Frame)中的操作栈深度。
                READ_U2(method->max_stack, ptr, len);
                /* max_locals代表了局部变量表所需的存储空间。
                    在这里，max_locals的单位是变量槽(Slot)，变量 槽是虚拟机为局部变量分配内存所使用的最小单位。
                    并不是在方法中用了多少个局部变量，就把这 些局部变量所占变量槽数量之和作为max_locals的值，
                    操作数栈和局部变量表直接决定一个该方法的栈 帧所耗费的内存，不
                    必要的操作数栈深度和变量槽数量会造成内存的浪费。
                    Java虚拟机的做法是将局 部变量表中的变量槽进行重用，当代码执行超出一个局部变量的作用域时，
                    这个局部变量所占的变量 槽可以被其他局部变量所使用，
                    Javac编译器会根据变量的作用域来分配变量槽给各个变量使用，
                    根据 同时生存的最大局部变量数量和类型计算出max_locals的大小。*/
                READ_U2(method->max_locals, ptr, len);

                // code_lengt h和code用来存储Java源程序编译后生成的字节码指令。
                // code_lengt h代表字节码长度
                // code是用于存储字节码指令的一系列字节流
                READ_U4(code_length, ptr, len); 
                method->code = sysMalloc(code_length);  // 分配内存
                memcpy(method->code, ptr, code_length); // 将code进行复制
                ptr += code_length;
                /* 
                    既然叫字节码指令，那顾名思义每个指令就是一个u1类 型的单字节，
                    当虚拟机读取到code中的一个字节码时，就可以对应找出这个字节码代表的是什么指令，
                    并且可以知道这条指令后面是否需要跟随参数，以及后续的参数应当如何解析。
                    我们知道一个u1 数据类型的取值范围为0x00~0xFF，对应十进制的0~255，
                    也就是一共可以表达256条指令。
                    目前，《Java虚拟机规范》已经定义了其中约200条编码值对应的指令含义， */
                method->code_size = code_length;

                // 在字节码指令之后的是这个方法的显式异常处理表集合，异常表对于Code属性来说并不是必须存在的
                READ_U2(method->exception_table_size, ptr, len);
                method->exception_table =
                        sysMalloc(method->exception_table_size *
                                  sizeof(ExceptionTableEntry));

                for(j = 0; j < method->exception_table_size; j++) {
                    /*
                        如果当字节码从第start_pc行[1]到第end_pc行之间(不含第end_pc行)
                        出现了类型为catch_type或者其子类的异常 (catch_type为指向一个CONSTANT_Class_info型常量的索引)，
                        则转到第handler_pc行继续处理。

                        当 catch_type的值为0时，代表任意异常情况都需要转到handler_pc处进行处理。*/
                    ExceptionTableEntry *entry = &method->exception_table[j];              
                    READ_U2(entry->start_pc, ptr, len);
                    READ_U2(entry->end_pc, ptr, len);
                    READ_U2(entry->handler_pc, ptr, len);
                    READ_U2(entry->catch_type, ptr, len);
                }
                // TODO 代码属性表是啥？
                READ_U2(code_attr_cnt, ptr, len);
                for(; code_attr_cnt != 0; code_attr_cnt--) {
                    u2 attr_name_idx;
                    u4 attr_length;

                    READ_TYPE_INDEX(attr_name_idx, constant_pool,
                                    CONSTANT_Utf8, ptr, len);
                    attr_name = CP_UTF8(constant_pool, attr_name_idx);
                    READ_U4(attr_length, ptr, len);

                    if(attr_name == SYMBOL(LineNumberTable)) {
                        READ_U2(method->line_no_table_size, ptr, len);
                        method->line_no_table =
                                sysMalloc(method->line_no_table_size *
                                          sizeof(LineNoTableEntry));

                        for(j = 0; j < method->line_no_table_size; j++) {
                            LineNoTableEntry *entry = &method->line_no_table[j];
                         
                            READ_U2(entry->start_pc, ptr, len);
                            READ_U2(entry->line_no, ptr, len);
                        }
                    } else
                        ptr += attr_length;
                }
            } else if(attr_name == SYMBOL(Exceptions)) { // 方法表 方法抛出的异常列表
                // Exceptions属性的作用是列举出方法中可能抛出的受查异常(Checked Excepitons)，也 就是方法描述时在throws关键字后面列举的异常。
                int j;
                // 异常数
                READ_U2(method->throw_table_size, ptr, len);
                method->throw_table = sysMalloc(method->throw_table_size *
                                                sizeof(u2));
                for(j = 0; j < method->throw_table_size; j++) {
                    // 每次读两个字节 获取异常定义
                    // except ion_index_table是一个指向常量池中 CONSTANT_Class_info型常量的索引，代表了该受查异常的类型。
                    READ_U2(method->throw_table[j], ptr, len);
                }

            } else if(attr_name == SYMBOL(Signature)) {
                u2 signature_idx;
                READ_TYPE_INDEX(signature_idx, constant_pool, CONSTANT_Utf8,
                                ptr, len);
                method->signature = CP_UTF8(constant_pool, signature_idx);

            } else if(attr_name == SYMBOL(RuntimeVisibleAnnotations)) {
                setIndexedAttribute(extra_attributes.method_annos,
                                    method - classblock->methods, ptr,
                                    attr_length, classblock->methods_count); 

                parseMethodAnnotations(constant_pool, method, ptr,
                                       attr_length);
                ptr += attr_length;

            } else if(attr_name == SYMBOL(RuntimeVisibleParameterAnnotations)) { // JDK5中新增的属性 为动态注解提供支持。该属性用于指明那些注解是运行时可见的
                setIndexedAttribute(extra_attributes.method_parameter_annos,
                                    method - classblock->methods, ptr,
                                    attr_length, classblock->methods_count); 
                ptr += attr_length;

            } else if(attr_name == SYMBOL(AnnotationDefault)) {// JDK5新增的属性 用于记录注解类元素的默认值
                setIndexedAttribute(extra_attributes.method_anno_default_val,
                                    method - classblock->methods, ptr,
                                    attr_length, classblock->methods_count); 
                ptr += attr_length;
#ifdef JSR308
            } else if(attr_name == SYMBOL(RuntimeVisibleTypeAnnotations)) { // jdk8新增的属性 为实现JSR 308中新增的类型注解提供的支持 用于指明那些类注解式运行时可见的
                setIndexedAttribute(extra_attributes.method_type_annos,
                                    method - classblock->methods, ptr,
                                    attr_length, classblock->methods_count); 
                ptr += attr_length;
#endif
#ifdef JSR901
            } else if(attr_name == SYMBOL(MethodParameters)) { // 1.8新增的属性 编译时加上-parmeters参数将方法名称编译进class文件中并可运行时获取
                setIndexedAttribute(extra_attributes.method_parameters,
                                    method - classblock->methods, ptr,
                                    attr_length, classblock->methods_count); 
                ptr += attr_length;
#endif
            } else
                ptr += attr_length;
        }
    }

    READ_U2(attr_count, ptr, len);
    for(; attr_count != 0; attr_count--) {
        u2 attr_name_idx;
        char *attr_name;
        u4 attr_length;

        READ_TYPE_INDEX(attr_name_idx, constant_pool, CONSTANT_Utf8, ptr, len);
        attr_name = CP_UTF8(constant_pool, attr_name_idx);
        READ_U4(attr_length, ptr, len);

        if(attr_name == SYMBOL(SourceFile)) {
            u2 file_name_idx;
            READ_TYPE_INDEX(file_name_idx, constant_pool, CONSTANT_Utf8,
                            ptr, len);
            classblock->source_file_name =
                                  CP_UTF8(constant_pool, file_name_idx);

        } else if(attr_name == SYMBOL(InnerClasses)) {
            // InnerClasses属性用于记录内部类与宿主类之间的关联。如果一个类中定义了内部类，那编译器将会为它以及它所包含的内部类生成InnerClasses属性。
            int j, size; // size代表需要记录多少个内部类信息，每一个内部类的信息都由一个 inner_classes_info表进行描述
            READ_U2(size, ptr, len);
            {
                u2 inner_classes[size];
                for(j = 0; j < size; j++) {
                    // inner和outer都是指向常量池中CONSTANT_Class_info型常量的索 引，分别代表了内部类和宿主类的符号引用。
                    int inner, outer;
                    READ_TYPE_INDEX(inner, constant_pool, CONSTANT_Class,
                                    ptr, len);
                    READ_TYPE_INDEX(outer, constant_pool, CONSTANT_Class,
                                    ptr, len);
                    // TODO 这里为啥内部类和外部类的初始化是不一样的？
                    if(inner == this_idx) {
                        int inner_name_idx;

                        classblock->declaring_class = outer;
                        // inner_name_index是指向常量池中CONSTANT _Ut f8_info型常量的索引，代表这个内部类的名称， 如果是匿名内部类，这项值为0。
                        READ_TYPE_INDEX(inner_name_idx, constant_pool,
                                        CONSTANT_Utf8, ptr, len);
                        if(inner_name_idx == 0)
                            classblock->flags |= ANONYMOUS;
                        // inner_class_access_flags是内部类的访问标志，类似于类的access_flags
                        READ_U2(classblock->inner_access_flags, ptr, len);
                    } else {
                        // TODO 为啥如果是外部类的话，这里要+4？
                        ptr += 4;
                        if(outer == this_idx)
                            inner_classes[classblock->inner_class_count++]
                                     = inner;
                    }
                }

                if(classblock->inner_class_count) {
                    classblock->inner_classes =
                                sysMalloc(classblock->inner_class_count *
                                          sizeof(u2));
                    memcpy(classblock->inner_classes, &inner_classes[0],
                           classblock->inner_class_count * sizeof(u2));
                }
            }
        } else if(attr_name == SYMBOL(EnclosingMethod)) {
            READ_TYPE_INDEX(classblock->enclosing_class, constant_pool,
                            CONSTANT_Class, ptr, len);
            READ_TYPE_INDEX(classblock->enclosing_method, constant_pool,
                            CONSTANT_NameAndType, ptr, len);

        } else if(attr_name == SYMBOL(Signature)) {
            /*
                Signature属性在JDK 5增加到Class文件规范之中，它是一个可选的定长属性，可以出现于类、字段 表和方法表结构的属性表中。
                在JDK 5里面大幅增强了Java语言的语法，在此之后，任何类、接口、初 始化方法或成员的泛型签名如果包含了类型变量(Type Variable)或参数化类型(Parameterized Type)，则Signature属性会为它记录泛型签名信息
                之所以要专门使用这样一个属性去记录泛型类型，是因为Java语言的泛型采用的是擦除法实现的伪泛型，字节码(Code属性)中所有的泛型信息编 译(类型变量、参数化类型)在编译之后都通通被擦除掉。
                Signat ure属性就是为了弥补这个缺陷而增设的，现在Java的反射API能够获取的泛型类型，最终的数据来源也是这个属性。
            */
           // signat ure_index项的值必须是一个对常量池的有效索引。常量池在该索引处的项必须是 CONSTANT_Utf8_info结构，表示类签名或方法类型签名或字段类型签名。
            // 如果当前的Signature属性 是类文件的属性，则这个结构表示类签名，如果当前的Signat ure属性是方法表的属性，则这个结构表 示方法类型签名，如果当前Signat ure属性是字段表的属性，则这个结构表示字段类型签名。
            u2 signature_idx;
            READ_TYPE_INDEX(signature_idx, constant_pool, CONSTANT_Utf8,
                            ptr, len);
            classblock->signature = CP_UTF8(constant_pool, signature_idx);

        } else if(attr_name == SYMBOL(Synthetic))
            // Synthetic属性代表此字段或者方法并不是由Java源码直接产生的，而是由编译器自行添加的，在 JDK 5之后，标识一个类、字段或者方法是编译器自动产生的，也可以设置它们访问标志中的 ACC_SYNTHETIC标志位。
            classblock->access_flags |= ACC_SYNTHETIC;

        else if(attr_name == SYMBOL(RuntimeVisibleAnnotations)) {
            // Runt imeVisibleAnnot at ions是一个变长属性，它记录了类、字段或方法的声明上记录运行时可见注 解，当我们使用反射API来获取类、字段或方法上的注解时，返回值就是通过这个属性来取到的。
            setSingleAttribute(extra_attributes.class_annos,
                               ptr, attr_length);
            ptr += attr_length;
#ifdef JSR308
        } else if(attr_name == SYMBOL(RuntimeVisibleTypeAnnotations)) {
            setSingleAttribute(extra_attributes.class_type_annos,
                               ptr, attr_length);
            ptr += attr_length;
#endif
#ifdef JSR292
        } else if(attr_name == SYMBOL(BootstrapMethods)) {
            /*
                BootstrapMethods属性在JDK 7时增加到Class文件规范之中，它是一个复杂的变长属性，位于类文件的属性表中。这个属性用于保存invokedy namic指令引用的引导方法限定符。
                
            */
           // TODO 这一块还不是很懂 需要跟着书仔细看看
            int num_methods, *offsets;
            u2 *indexes;
            char *data;
            
            // 方法数量
            READ_U2(num_methods, ptr, len);

            data = sysMalloc(attr_length + num_methods*2 + 2);
            indexes = (u2*)(data + num_methods*4 + 4);
            offsets = (int*)data;

            for(; num_methods != 0; num_methods--) {
                // method_ref： 一个指向常量池CONSTANT_MethodHandle结构的索引值，它代表了一个引导方法。常量池在该 索引处的值必须是一个CONSTANT_MethodHandle_info结构。
                // num_args： 方法的数量
                int method_ref, num_args;
                READ_U2(method_ref, ptr, len);
                READ_U2(num_args, ptr, len);

                *offsets++ = (char*)indexes - data;
                *indexes++ = method_ref;

                for(; num_args != 0; num_args--) {
                    int arg_idx;
                    READ_U2(arg_idx, ptr, len);
                    *indexes++ = arg_idx;
                }
            }

            *offsets++ = (char*)indexes - data;
            classblock->bootstrap_methods = data;
#endif
        } else
            ptr += attr_length;
    }

    for(i = 0; i < sizeof(ExtraAttributes)/sizeof(void*)
                      && extra_attributes.data[i] == NULL; i++);

    if(i < sizeof(ExtraAttributes)/sizeof(void*)) {
        classblock->extra_attributes = sysMalloc(sizeof(ExtraAttributes));
        memcpy(classblock->extra_attributes, &extra_attributes,
                                             sizeof(ExtraAttributes));
    }

    if(super_idx) {
        // 有父类 解析一下父类
        classblock->super = resolveClass(class, super_idx, FALSE, FALSE);
        if(exceptionOccurred())
           return NULL;
    }
    
    classblock->state = CLASS_LOADED;
    return class;
}

/*
    定义一个class
    根据传入的classname和Class的字节流以及类加载器，先去解析class，然后把解析的class放入一张hash表中，避免重复定义

*/
Class *defineClass(char *classname, char *data, int offset, int len,
                   Object *class_loader) {

    // 解析class
    Class *class = parseClass(classname, data, offset, len, class_loader);

    if(class != NULL) {
        // 将解析出来的Class信息添加到hash表中 这样下次就可以直接获取而不需要再次解析了
        Class *found = addClassToHash(class, class_loader);

        // 判断是否有重复的Class定义
        if(found != class) {
            // 设置class的flags为定义冲突
            CLASS_CB(class)->flags = CLASS_CLASH;
            if(class_loader != NULL) {
                signalException(java_lang_LinkageError,
                                "duplicate class definition");
                return NULL;
            }
            return found;
        }
    }

    return class;
}

Class *createArrayClass(char *classname, Object *class_loader) {
    Class *comp_class, *elem_class, *class, *found = NULL;
    ClassBlock *elem_cb, *classblock;
    int dim;

    class = allocClass();
    if(class == NULL)
        return NULL;

    classblock = CLASS_CB(class);

    classblock->name = copyUtf8(classname);
    classblock->super = findSystemClass0(SYMBOL(java_lang_Object));
    classblock->method_table = CLASS_CB(classblock->super)->method_table;

    classblock->interfaces_count = 2;
    classblock->interfaces = sysMalloc(sizeof(Class*) * 2);
    classblock->interfaces[0] = findSystemClass0(SYMBOL(java_lang_Cloneable));
    classblock->interfaces[1] = findSystemClass0(SYMBOL(java_io_Serializable));

    classblock->state = CLASS_ARRAY;

    /* Find the array element class and the dimension --
       this is used to speed up type checking (instanceof) */

    if(classname[1] == '[') {
        comp_class = findArrayClassFromClassLoader(classname + 1,
                                                   class_loader);
        if(comp_class == NULL)
            goto error;

        elem_class = CLASS_CB(comp_class)->element_class;
        dim = CLASS_CB(comp_class)->dim + 1;
    } else { 
        if(classname[1] == 'L') {
            int len = strlen(classname);
            char element_name[len - 2];

            memcpy(element_name, classname + 2, len - 3);
            element_name[len - 3] = '\0';

            elem_class = findClassFromClassLoader(element_name, class_loader);
        } else
            elem_class = findPrimitiveClass(classname[1]);

        if(elem_class == NULL)
            goto error;

        comp_class = elem_class;
        dim = 1;
    }

    classblock->component_class = comp_class;
    classblock->element_class = elem_class;
    classblock->dim = dim;

    elem_cb = CLASS_CB(elem_class);

    /* The array's classloader is the loader of the element class */
    classblock->class_loader = elem_cb->class_loader;

    /* The array's visibility (i.e. public, etc.) is that of the element */
    classblock->access_flags = (elem_cb->access_flags & ~ACC_INTERFACE) |
                               ACC_FINAL | ACC_ABSTRACT;

    prepareClass(class);

    found = addClassToHash(class, classblock->class_loader);
    if(found == class) {
        if(verbose)
            jam_printf("[Created array class %s]\n", classname);
        return class;
    }

error:
    classblock->flags = CLASS_CLASH;
    return found;
}

Class *createPrimClass(char *classname, int index) {
    Class *class;
    ClassBlock *classblock;
 
    if((class = allocClass()) == NULL)
        return NULL;

    classblock = CLASS_CB(class);
    classblock->name = classname;
    classblock->state = CLASS_PRIM + index;
    classblock->access_flags = ACC_PUBLIC | ACC_FINAL | ACC_ABSTRACT;

    prepareClass(class);

    lockHashTable(boot_classes);
    if(prim_classes[index] == NULL)
        prim_classes[index] = class;
    unlockHashTable(boot_classes);

    if(verbose)
        jam_printf("[Created primitive class %s]\n", classname);

    return prim_classes[index];
}

/* Layout the instance data.

   The object layout places 64-bit fields on a double-word boundary
   as on some architectures this leads to better performance (and
   misaligned loads/store may cause traps).

   Reference fields are also placed together as these must be scanned
   during GC, and placing them together reduces the number of entries
   required in the reference offsets list.

   Double/long fields are layed out first, then references and finally
   int-sized fields.  When padding is needed for 64-bit fields we try
   to place an int-sized field, and only leave a hole when no int-sized
   fields are available */

void prepareFields(Class *class) {
    ClassBlock *cb = CLASS_CB(class);
    Class *super = (cb->access_flags & ACC_INTERFACE) ? NULL
                                                      : cb->super;
    RefsOffsetsEntry *spr_rfs_offsts_tbl = NULL;
    int spr_rfs_offsts_sze = 0;

    FieldBlock *ref_head = NULL;
    FieldBlock *int_head = NULL;
    FieldBlock *dbl_head = NULL;

    int field_offset = sizeof(Object);
    int refs_start_offset = 0;
    int refs_end_offset = 0;
    int i;

    if(super != NULL) {
        field_offset = CLASS_CB(super)->object_size;
        spr_rfs_offsts_sze = CLASS_CB(super)->refs_offsets_size;
        spr_rfs_offsts_tbl = CLASS_CB(super)->refs_offsets_table;
    }

    /* Initialise static fields to default value, and separate
       instance fields into three linked lists, holding
       int-sized fields, double-sized fields and reference
       fields */

    for(i = 0; i < cb->fields_count; i++) {
        FieldBlock *fb = &cb->fields[i];

        if(fb->access_flags & ACC_STATIC)
            fb->u.static_value.l = 0;
        else {
            FieldBlock **list;

            if(fb->type[0] == 'L' || fb->type[0] == '[')
                list = &ref_head;
            else
                if(fb->type[0] == 'J' || fb->type[0] == 'D')
                    list = &dbl_head;
                else
                    list = &int_head;

            fb->u.static_value.p = *list;
            *list = fb;
        }

        fb->class = class;
    }

    /* Layout the double-sized fields.  If padding is required,
       use the first int-sized field (int_list head), or leave
       a hole if no int-fields */

    if(dbl_head != NULL) {
        if(field_offset & 0x7) {
            if(int_head != NULL) {
                FieldBlock *fb = int_head;
                int_head = int_head->u.static_value.p;
                fb->u.offset = field_offset;
            }
            field_offset += 4;
        }

        do {
            FieldBlock *fb = dbl_head;
            dbl_head = dbl_head->u.static_value.p;
            fb->u.offset = field_offset;
            field_offset += 8;
        } while(dbl_head != NULL);
    }

    /* Layout the reference fields.  If padding is required,
       use an int-sized field (int_list head), or leave
       a hole if no int-fields remaining */

    if(ref_head != NULL) {
        if(sizeof(Object*) == 8 && field_offset & 0x7) {
            if(int_head != NULL) {
                FieldBlock *fb = int_head;
                int_head = int_head->u.static_value.p;
                fb->u.offset = field_offset;
            }
            field_offset += 4;
        }

        refs_start_offset = field_offset;

        do {
            FieldBlock *fb = ref_head;
            ref_head = ref_head->u.static_value.p;
            fb->u.offset = field_offset;
            field_offset += sizeof(Object*);
        } while(ref_head != NULL);

        refs_end_offset = field_offset;
    }

    /* Layout the remaining int-sized fields */

    while(int_head != NULL) {
        FieldBlock *fb = int_head;
        int_head = int_head->u.static_value.p;
        fb->u.offset = field_offset;
        field_offset += 4;
    }

   cb->object_size = field_offset;

   /* Construct the reference offsets list.  This is used to speed up
      scanning of an objects references during the mark phase of GC.
      If possible, merge the entry with the previous entry */

   if(refs_start_offset) {
       if(spr_rfs_offsts_sze > 0 && spr_rfs_offsts_tbl[spr_rfs_offsts_sze-1].end
                                           == refs_start_offset) {

           cb->refs_offsets_size = spr_rfs_offsts_sze;
           refs_start_offset = spr_rfs_offsts_tbl[spr_rfs_offsts_sze-1].start;
       } else
           cb->refs_offsets_size = spr_rfs_offsts_sze + 1;

      cb->refs_offsets_table = sysMalloc(cb->refs_offsets_size *
                                         sizeof(RefsOffsetsEntry));

      memcpy(cb->refs_offsets_table, spr_rfs_offsts_tbl,
             spr_rfs_offsts_sze * sizeof(RefsOffsetsEntry));

      cb->refs_offsets_table[cb->refs_offsets_size-1].start = refs_start_offset;
      cb->refs_offsets_table[cb->refs_offsets_size-1].end = refs_end_offset;
   } else {
       cb->refs_offsets_size = spr_rfs_offsts_sze;
       cb->refs_offsets_table = spr_rfs_offsts_tbl;
   }
}
// TODO 隐藏字段 从 GC？
int hideFieldFromGC(FieldBlock *hidden) {
    ClassBlock *cb = CLASS_CB(hidden->class);
    FieldBlock *fb;
    int i;

    for(fb = cb->fields, i = 0; i < cb->fields_count; i++,fb++)
        if(fb->u.offset > hidden->u.offset)
            fb->u.offset -= sizeof(Object*);

    cb->refs_offsets_table[cb->refs_offsets_size-1].end -= sizeof(Object*);

    return hidden->u.offset = cb->object_size - sizeof(Object*);
}

#define fillinMTable(method_table, methods, methods_count)              \
{                                                                       \
    int i;                                                              \
    for(i = 0; i < methods_count; i++, methods++) {                     \
        if((methods->access_flags & (ACC_STATIC | ACC_PRIVATE)) ||      \
               (methods->name[0] == '<'))                               \
            continue;                                                   \
        method_table[methods->method_table_index] = methods;            \
    }                                                                   \
}

#define MRNDA_CACHE_INCR 16

/* Structure of each Miranda cache entry.  The Miranda
   cache holds details of new Miranda methods found during
   the construction of the interface method table. 
   每个Miranda缓存条目的结构。Miranda缓存保存了在构建接口方法表期间发现的新的Miranda方法的详细信息。*/

typedef struct miranda {
    MethodBlock *mb;
    int mtbl_idx;
    int default_conflict;
} Miranda;

/*
    在JVM那本书里面，链接是三个过程（验证、准备、解析）的统称。

    在这个方法里面，主要做的事情，是对方法的处理
        1. 本地方法的链接
        2. 接口方法的处理（将接口的方法统一放到method_table中，每一个表项中有指向接口的方法）
        3. Miranda方法的处理
        4. 终结器方法的处理
        5. 引用（Reference）相关方法的处理（不正确）
        6. ClassLoader相关处理
    
    嗯....还是有点云里雾里
    总感觉链接的意思，是把Class当中，所有对其他类的引用从一个符号给变成一个指针
*/
void linkClass(Class *class) {
   static MethodBlock *obj_fnlzr_mthd = NULL;

   ClassBlock *cb = CLASS_CB(class);
   // 判断是否是接口 是接口的话没有super（emmm 接口也有的吧？或者说这里的super必须是一个类？）
   Class *super = (cb->access_flags & ACC_INTERFACE) ? NULL : cb->super;

   ITableEntry *spr_imthd_tbl = NULL;
   MethodBlock **method_table = NULL;
   MethodBlock **spr_mthd_tbl = NULL;
   MethodBlock *mb;

   int new_methods_count = 0;
   int spr_imthd_tbl_sze = 0;
   int itbl_offset_count = 0;
   int spr_mthd_tbl_sze = 0;
   int method_table_size;
   int new_itable_count;
   int itbl_idx, i, j;
   int spr_flags = 0;
   Thread *self;

   // 如果已经链接过了 那就跳过吧
   if(cb->state >= CLASS_LINKED)
       return;
   
   // 加锁
   objectLock(class);

   if(cb->state >= CLASS_LINKED)
       goto unlock;

   if(verbose)
       jam_printf("[Linking class %s]\n", cb->name);

   if(super) {
      // 如果存在父类 且父类暂未进行过链接 则先把父类的链接给做了
      ClassBlock *super_cb = CLASS_CB(super);
      if(super_cb->state < CLASS_LINKED)
          linkClass(super);

      spr_flags = super_cb->flags;
      spr_mthd_tbl = super_cb->method_table;
      spr_imthd_tbl = super_cb->imethod_table;
      spr_mthd_tbl_sze = super_cb->method_table_size;
      spr_imthd_tbl_sze = super_cb->imethod_table_size;
   }

   /* Calculate object layout 计算对象布局*/
   // TODO 里面做了啥
   prepareFields(class);

   /* Prepare methods 准备方法 */

   for(mb = cb->methods, i = 0; i < cb->methods_count; i++,mb++) {
       int count = 0;
       char *sig = mb->type;

       /* calculate argument count from signature 从签名计算参数数量*/
       SCAN_SIG(sig, count+=2, count++);

       // 如果是static的方法 则没有this这个隐式参数
       if(mb->access_flags & ACC_STATIC)
           mb->args_count = count;
       else
           mb->args_count = count + 1;
       
       // set class to mb
       mb->class = class;

       /* Set abstract method to stub 设置抽象方法为存根*/
       // TODO 啥意思？
       if(mb->access_flags & ACC_ABSTRACT) {
           mb->code_size = sizeof(abstract_method);
           mb->code = abstract_method;
       }
       
       // 判断是否是一个native方法
       if(mb->access_flags & ACC_NATIVE) {

           /* set up native invoker to wrapper to resolve function 
              on first invocation 
              设置本地调用方法（native_invoker）为一个包装方法，在第一次调用时解析函数。*/

           mb->native_invoker = &resolveNativeWrapper;

           /* native methods have no code attribute so these aren't filled
              in at load time - as these values are used when creating frame
              set to appropriate values 
              原生方法没有代码属性，所以在加载时不会填入这些值--因为这些值在创建框架设置为适当的值时使用。*/

           mb->max_locals = mb->args_count;
           mb->max_stack = 0;
       }

       /* Static, private or init methods aren't dynamically invoked, so
         don't stick them in the table to save space 
         静态方法、私有方法或init方法不是动态调用的，所以不要把它们贴在表中以节省空间 */

       if((mb->access_flags & (ACC_STATIC | ACC_PRIVATE)) ||
                              (mb->name[0] == '<'))
           continue;

       /* if it's overriding an inherited method, replace in method table 
        如果它覆盖了一个继承的方法，在方法表中替换 */

       for(j = 0; j < spr_mthd_tbl_sze; j++)
           if(mb->name == spr_mthd_tbl[j]->name && // 判断名称
                        mb->type == spr_mthd_tbl[j]->type && // 判断类型
                        checkMethodAccess(spr_mthd_tbl[j], class)) { // 判断方法的访问权限是可以访问的话
               // 替换方法表的index为父方法的index
               mb->method_table_index = spr_mthd_tbl[j]->method_table_index;
               break;
           }
       // 如果j == spr_mthd_tbl_sze 那么肯定是在父方法表没有找到对应的
       if(j == spr_mthd_tbl_sze)
           // 设置角标 = 父表的数量 + 当前类已经存在的方法数量 （秒啊）
           mb->method_table_index = spr_mthd_tbl_sze + new_methods_count++;
   }

   /* construct method table 构造方法表*/
   // TODO 这个设置是为啥 为啥不拿mb->method_table_index的数据
   method_table_size = spr_mthd_tbl_sze + new_methods_count;

   // 判断是否是一个接口的方法
   if(!(cb->access_flags & ACC_INTERFACE)) {
       // 不是的话 分配内存了
       method_table = sysMalloc(method_table_size * sizeof(MethodBlock*));

       /* Copy parents method table to the start 将父母方法表复制到起始位置 */
       memcpy(method_table, spr_mthd_tbl, spr_mthd_tbl_sze *
                                          sizeof(MethodBlock*));

       /* fill in the additional methods -- we use a
          temporary because fillinMtable alters mb 
          填入额外的方法 -- 我们使用一个临时的方法，因为 fillinMtable 改变 mb */
       mb = cb->methods;
       fillinMTable(method_table, mb, cb->methods_count);
   }

   /* setup interface method table 设置接口方法表*/

   /* number of interfaces implemented by this class is those implemented by
    * parent, plus number of interfaces directly implemented by this class,
    * and the total number of their superinterfaces 
    * 该类实现的接口数是指父类实现的接口数，加上该类直接实现的接口数，以及其超级接口的总数。 */

   new_itable_count = cb->interfaces_count;
   for(i = 0; i < cb->interfaces_count; i++)
       new_itable_count += CLASS_CB(cb->interfaces[i])->imethod_table_size;

   cb->imethod_table = sysMalloc((spr_imthd_tbl_sze + new_itable_count) *
                                 sizeof(ITableEntry));

   /* the interface references in the imethod table are updated by the
      GC during heap compaction - disable suspension while it is being
      setup 
      在堆压缩过程中，GC会更新imethod_table中的接口引用--在设置时禁止暂停。*/
   // 应该是避免GC触发压缩导致imethod_table中的接口引用错乱
   self = threadSelf();
   fastDisableSuspend(self);
   // 接口方法表的大小肯定就是当前的+父类的啦
   cb->imethod_table_size = spr_imthd_tbl_sze + new_itable_count;

   /* copy parent's interface table - the offsets into the method
      table won't change 
      复制父类的接口表--方法表中的偏移量不会改变。*/

   memcpy(cb->imethod_table, spr_imthd_tbl, spr_imthd_tbl_sze *
                                            sizeof(ITableEntry));

   /* now run through the extra interfaces implemented by this class,
    * fill in the interface part, and calculate the number of offsets
    * needed (i.e. the number of methods defined in the interfaces) 
    * 现在运行该类实现的额外接口，填写接口部分，并计算所需的偏移数（即接口中定义的方法数）。*/

   itbl_idx = spr_imthd_tbl_sze;  // TODO 这里为啥要加上父类实现的接口数量？ 难道说父类实现的接口方法需要在子类的方法表这里也保存一遍？应该不会这么设计吧
   for(i = 0; i < cb->interfaces_count; i++) {
       Class *intf = cb->interfaces[i]; // intf 是迭代当前class的接口变量
       ClassBlock *intf_cb = CLASS_CB(intf);

       cb->imethod_table[itbl_idx++].interface = intf;  // 设置当前class的接口方法表的每一个表项对应的接口Class指针
       itbl_offset_count += intf_cb->method_table_size;  // 加上当前接口的方法数量

       for(j = 0; j < intf_cb->imethod_table_size; j++) { // 然后又是迭代当前类的接口所继承的接口....
           Class *spr_intf = intf_cb->imethod_table[j].interface;

           cb->imethod_table[itbl_idx++].interface = spr_intf;  // 在把接口的接口给赋值到当前类的接口方法表中
           itbl_offset_count += CLASS_CB(spr_intf)->method_table_size; // 加上当前接口的接口的方法数量
       }
   }

   // 执行完上面的循环之后，当前类的imethod_table 就包含了所有实现的接口的Class指针，以及itbl_offset_count就是所有实现的接口的方法数量了

   // 这个时候可以开启了，让GC可以可以进行压缩了
   fastEnableSuspend(self);

   /* if we're an interface all finished - offsets aren't used 如果我们是一个接口，一切都结束了--不使用偏移量。*/

   if(!(cb->access_flags & ACC_INTERFACE)) {  
       // 如果不是一个接口类

       // 分配内存 itbl_offset_count就是接口的数量
       int *offsets_pntr = sysMalloc(itbl_offset_count * sizeof(int));
       Miranda *mirandas = NULL;
       int new_mtbl_count = 0;
       int miranda_count = 0;

       /* run through table again, this time filling in the offsets array -
        * for each new interface, run through it's methods and locate
        * each method in this classes method table 
        * 再次遍历表格，这次填入偏移量数组--对于每个新的接口，遍历它的方法，在这个类的方法表中找到每个方法。 */
       
       // 迭代接口表
       for(i = spr_imthd_tbl_sze; i < cb->imethod_table_size; i++) {
           Class *interface = cb->imethod_table[i].interface;
           ClassBlock *intf_cb = CLASS_CB(interface);

           cb->imethod_table[i].offsets = offsets_pntr;

           // 迭代接口的方法
           for(j = 0; j < intf_cb->methods_count; j++) {
               MethodBlock *intf_mb = &intf_cb->methods[j];
               int mtbl_idx, mrnda_idx;

               // 如果接口是私有的或者静态的 或者是构造方法 跳过就行了
               if((intf_mb->access_flags & (ACC_STATIC | ACC_PRIVATE)) ||
                      (intf_mb->name[0] == '<'))
                   continue;

               /* We scan backwards so that we find methods defined in
                  sub-classes before super-classes.  This ensures we find
                  non-overridden methods before the inherited non-accessible
                  method 
                  我们向后扫描以便在超类之前找到子类中定义的方法。这确保我们在继承的不可访问方法之前找到非覆盖的方法 */

               // 说人话就是 优先查找当前当前类的方法表 判断是否已经实现了
               for(mtbl_idx = method_table_size - 1; mtbl_idx >= 0; mtbl_idx--)
                   if(intf_mb->name == method_table[mtbl_idx]->name &&
                           intf_mb->type == method_table[mtbl_idx]->type)
                       break;

               /* If we found the method in the method table and it's
                  a real method (i.e. implemented) or we already have
                  conflicting defaults we're done 
                  如果我们在方法表中找到了方法，并且它是一个真实的方法(即实现的方法)，或者我们已经有了冲突的默认值，我们就完成了*/
               if(mtbl_idx >= 0) {
                   MethodBlock *mb = method_table[mtbl_idx];
                   // 判断访问权限 这两个访问权限是啥
                   if(!(mb->access_flags & ACC_MIRANDA) ||
                                      mb->flags & MB_DEFAULT_CONFLICT) {
                       // 如果当前查找出来的方法权限校验通过 则将当前方法的在方法表的角标 设置为当前接口表项的offset
                       // 并且offsets_pntr+1 这样的话，下一个表项就可以继续使用了
                       *offsets_pntr++ = mtbl_idx;
                       continue;
                   }
               }

               /* We didn't find it, or we already have a matching
                  inherited Miranda method.  Search the Miranda cache
                  to see if we have cached a new Miranda method (either
                  a new Miranda or an override - see below) 
                  我们没有找到它，或者我们已经有了匹配的继承米兰达方法。
                  搜索米兰达缓存，看看我们是否缓存了一个新的米兰达方法(一个新的米兰达或覆盖-见下文)*/

               // Miranda是啥？
               /*
                 在早期的虚拟机中，有一个bug--虚拟机不走接口的类中寻找方法，他们只走了超类的链。 
                 这意味着只在接口中定义了抽象方法的没有被发现。 
                 为了解决这个bug，编译器中引入了一个反bug--所谓的Miranda方法。 
                 如果一个类在它的一个接口中没有提供一个抽象方法的定义，
                 那么编译器就会人为地在类中插入一个抽象方法。    
                 这样虚拟机就不用费心去看接口了。
    
                这是个问题。 Miranda方法不是规范的一部分。    但是它们继续被插入，这样老的VM就可以运行新的代码了。
                有一天，当老的VM不在的时候，也许类可以被编译成     没有Miranda方法。 为了达到这个目的，编译器有一个     标志，-nomiranda，它可以关闭这些方法的创建。   最终这种行为应该成为默认的。
    
                为什么它们被称为米兰达方法？ 那么这句话 "如果类不能提供方法，那么将提供一个方法。    的编译器"，这句话与 "如果你不能够     负担得起律师的费用，法院将提供一名律师，" -- -- 一个。    美国所谓的 "米兰达 "权利。
                
                说人话就是
                    public interface IA{
                        void test();
                    }
                    abstract class C implements IA{
                        public C(){
                            test();
                        }
                    }

                继承了IA接口但是没有实现test()方法.在编译期间却没有报错,因为当invokevirtual时,默认编译器会帮你添加一个miranda方法
                */
               for(mrnda_idx = 0; mrnda_idx < miranda_count; mrnda_idx++)
                   if(intf_mb->name == mirandas[mrnda_idx].mb->name &&
                               intf_mb->type == mirandas[mrnda_idx].mb->type)
                       break;
                           
               if(mrnda_idx == miranda_count) { // 没有搜索到缓存
                   int new_mtbl_idx, default_conflict = FALSE;

                   /* No cached Miranda method.  If we found a Miranda
                      method in the method table we need to check it
                      against the interface method to see if either one
                      extends the other.  If the method table Miranda
                      extends the new method it takes precedence and we
                      don't need a new Miranda.  If, however, the new method
                      extends the method table Miranda, or they are
                      unrelated, we need to add a new Miranda to override
                      the method table Miranda (with the new Miranda, or a
                      default conflict). 
                      没有缓存的米兰达方法。 如果我们在方法表中发现了一个Miranda方法，
                      我们需要将它与接口方法进行对照，看看是否有一个方法扩展了另一个方法。 
                      如果方法表Miranda扩展了新方法，它就会优先，我们不需要新建Miranda。
                       但是，如果新的方法扩展了方法表Miranda，或者它们是不相关的，
                       我们需要添加一个新的Miranda来覆盖方法表Miranda（用新的Miranda，或者默认冲突）。*/

                   if(mtbl_idx >= 0) { // mtbl_idx >= 0说明当前类有这个方法定义 一般是下面的代码进行设置过才有
                       // 获取这个方法
                       MethodBlock *mtbl_mb = method_table[mtbl_idx];

                       if(((mtbl_mb->access_flags & ACC_ABSTRACT) // TODO 如果是一个接口的方法 那么他也是抽象的吗？
                                   && (intf_mb->access_flags & ACC_ABSTRACT))  // 判断这个方法是抽象的 同时当前迭代的接口方法也是抽象的
                             || mtbl_mb->miranda_mb->class == interface // 当前的Miranda类为当前迭代的接口
                             || implements(interface, mtbl_mb->miranda_mb->class)) { // 或者当前的Miranda类是当前迭代接口的子类
                           // 设置这个抽象方法的实现为为当前mtbl_idx对应的方法
                           *offsets_pntr++ = mtbl_idx;
                           continue;
                       }
                       
                       // 如果这里 已经创建过的Miranda方法，不是当前迭代的接口，那么就说明有多个结构定义了这个方法，之前创建的Miranda需要设置为冲突
                       if(!implements(mtbl_mb->miranda_mb->class, interface))
                           default_conflict = TRUE;

                       /* The new Miranda is an override, so it replaces
                          the method in the method table 
                          新的Miranda是一个覆盖，所以它取代了方法表中的方法。*/
                       new_mtbl_idx = mtbl_idx;
                   } else {
                       /* No cached Miranda, and none in the method table -
                          simply add a new Miranda - it has a new method
                          table index 
                          没有缓存的Miranda，方法表中也没有--只需添加一个新的Miranda--它有一个新的方法表索引。*/
                        // 说人话就是 需要新建一个方法索引
                       new_mtbl_idx = method_table_size + new_mtbl_count++;
                   }

                   /* Extend the Miranda cache if it's full 如果Miranda缓存已满，则扩展缓存。*/
                   // MRNDA_CACHE_INCR = 16
                   if((miranda_count % MRNDA_CACHE_INCR) == 0)
                       mirandas = sysRealloc(mirandas, (miranda_count +
                                     MRNDA_CACHE_INCR) * sizeof(Miranda));

                   /* Add the new Miranda to the cache 将新的米兰达添加到缓存中*/
                   mirandas[miranda_count].mb = intf_mb;
                   mirandas[miranda_count].mtbl_idx = new_mtbl_idx;
                   mirandas[miranda_count].default_conflict = default_conflict;
                   miranda_count++;
               } else {
                   // 搜索到了缓存
                   /* Found a matching Miranda method in the cache (either
                      an override or a new Miranda).  Check the two methods
                      against each other as above.  The difference is if
                      we need to override, we simply modify the cached
                      Miranda - we don't need to add a new Miranda for the
                      override 
                      在缓存中找到一个匹配的Miranda方法（无论是覆盖还是新的Miranda）。 
                      对照上面的两种方法进行检查。 
                      不同的是，如果我们需要覆盖，我们只需修改缓存的Miranda--我们不需要为覆盖添加一个新的Miranda。*/
                   MethodBlock *mrnda_mb = mirandas[mrnda_idx].mb;

                   if(!((mrnda_mb->access_flags & ACC_ABSTRACT) 
                                && (intf_mb->access_flags & ACC_ABSTRACT))
                         && !mirandas[mrnda_idx].default_conflict
                         && mrnda_mb->class != interface
                         && !implements(interface, mrnda_mb->class)) {

                       if(implements(mrnda_mb->class, interface))
                           mirandas[mrnda_idx].mb = intf_mb;
                       else
                           mirandas[mrnda_idx].default_conflict = TRUE;
                   }
               }

               *offsets_pntr++ = mirandas[mrnda_idx].mtbl_idx;
           }
       }

       if(miranda_count > 0) {
           /* We've created some new Miranda methods.  Add them to
              the method area.  The method table may also need expanding
              if they are not all overrides 
              我们已经创造了一些新的米兰达方法。将它们添加到方法区域。
              如果方法表不是全部覆盖，则可能还需要展开*/
           
           mb = sysRealloc(cb->methods, (cb->methods_count + miranda_count)
                                        * sizeof(MethodBlock));

           /* If the realloc of the method area gave us a new pointer, the
              pointers to them in the method table are now wrong. 
              如果方法区域的realloc给了我们一个新指针，那么方法表中指向它们的指针现在是错误的。*/
           if(mb != cb->methods) {
               /*  mb will be left pointing to the end of the methods 
               mb将左边指向方法的末尾 */
               cb->methods = mb;
               fillinMTable(method_table, mb, cb->methods_count);
           } else
               // 否则的话指针移动到尾部 用来添加新的Miranda方法
               mb += cb->methods_count;
           // 初始化
           memset(mb, 0, miranda_count * sizeof(MethodBlock));

           // 同样的 给方法表重新分配内存
           if(new_mtbl_count > 0) {
               method_table_size += new_mtbl_count;
               method_table = sysRealloc(method_table, method_table_size *
                                                       sizeof(MethodBlock*));
           }

           /* Now we've expanded the methods, run through the Miranda
              cache and fill them in 
              现在我们已经扩展了方法，运行Miranda缓存并填充它们*/
           for(i = 0; i < miranda_count; i++,mb++) {
               MethodBlock *intf_mb = mirandas[i].mb;

               mb->class = class;
               mb->name = intf_mb->name;
               mb->type = intf_mb->type;
               mb->max_stack = intf_mb->max_stack;
               mb->max_locals = intf_mb->max_locals;
               mb->args_count = intf_mb->args_count;
               mb->method_table_index = mirandas[i].mtbl_idx;
               mb->access_flags = intf_mb->access_flags | ACC_MIRANDA;

               if(mirandas[i].default_conflict) {  // 如果冲突了
                   mb->flags = MB_DEFAULT_CONFLICT;
                   mb->code_size = sizeof(abstract_method);  // 大小用的是抽象方法的大小
                   mb->code = abstract_method; // 然后code是抽象代码数组 如果调用会报错
               } else {  // 不冲突的话 会进行桥接
                   // Miranda方法的字节码。如果它被调用，它将执行它所表示的未实现的接口方法。 
                   mb->miranda_mb = intf_mb;
                   mb->code_size = sizeof(miranda_bridge);
                   mb->code = miranda_bridge;
               }
               // 设置到方法表当中
               method_table[mirandas[i].mtbl_idx] = mb;
           }

           sysFree(mirandas);
           // 然后就是增加方法数量嘛
           cb->methods_count += miranda_count;
       }
   }
   
   // 给class block赋值
   cb->method_table = method_table;
   cb->method_table_size = method_table_size;

   /* Handle finalizer 处理终结器 */

   /* If this is Object find the finalize method.  All subclasses will
      have it in the same place in the method table.  Note, Object
      should always have a valid finalizer -- but check just in case 
      如果是对象，找到finalize方法。所有子类都将它放在方法表的相同位置。注意，Object应该总是有一个有效的终结器——但要检查以防万一*/

   if(cb->super == NULL) {
       MethodBlock *finalizer = findMethod(class, SYMBOL(finalize),
                                                  SYMBOL(___V));

       if(finalizer != NULL && !(finalizer->access_flags &
                                 (ACC_STATIC | ACC_PRIVATE))) {
           finalize_mtbl_idx = finalizer->method_table_index;
           obj_fnlzr_mthd = finalizer;
       }
   }

   cb->flags |= spr_flags;

   /* Mark as finalized only if it's overridden Object's finalizer.
      We don't want to finalize every object, and Object's imp is empty 
      只有当它覆盖了对象的终结器时，才标记为finalize。我们不想完成每个对象，而且object的imp是空的 */

   if(super != NULL && obj_fnlzr_mthd != NULL &&
          method_table[finalize_mtbl_idx] != obj_fnlzr_mthd) // TODO 这里不是很懂 为啥方法表中 终结器方法的角标对应的方法和终结器方法不能是同一个才设置为含有终结器？
          // TODO 如果有终结方法 那么说明method_table肯定也有这个方法啊，那么应该是一样的才对啊？为啥不一样才算覆盖啊？
       // 设置当前对象有终结器
       cb->flags |= FINALIZED;

   /* Handle reference classes 处理句柄引用类 */
   
   // TODO 这个句柄引用不是很看的懂  可以自己实现一个Reference调试看看
   if(ref_referent_offset == -1 &&
               cb->name == SYMBOL(java_lang_ref_Reference)) {

       FieldBlock *ref_fb = findField(class, SYMBOL(referent),
                                             SYMBOL(sig_java_lang_Object));
       FieldBlock *queue_fb = findField(class, SYMBOL(queue),
                                     SYMBOL(sig_java_lang_ref_ReferenceQueue));
       MethodBlock *enqueue_mb = findMethod(class, SYMBOL(enqueue),
                                                   SYMBOL(___Z));
       // 三者缺一不可
       if(ref_fb == NULL || queue_fb == NULL || enqueue_mb == NULL) {
           jam_fprintf(stderr, "Expected fields/methods missing in"
                               " java.lang.ref.Reference\n");
           exitVM(1);
       }
       // TODO 从GC隐藏字段？
       ref_referent_offset = hideFieldFromGC(ref_fb);
       enqueue_mtbl_idx = enqueue_mb->method_table_index;
       ref_queue_offset = queue_fb->u.offset;

       cb->flags |= REFERENCE;
   }
   
   // 如果父类是一个引用类型
   if(spr_flags & REFERENCE) {
       // 判断子类的引用类型
       if(cb->name == SYMBOL(java_lang_ref_SoftReference))  // 软引用
           cb->flags |= SOFT_REFERENCE;
       else
           if(cb->name == SYMBOL(java_lang_ref_WeakReference))   // 弱应用
               cb->flags |= WEAK_REFERENCE;
           else
               if(cb->name == SYMBOL(java_lang_ref_PhantomReference)) // 虚引用
                   cb->flags |= PHANTOM_REFERENCE;
   }

   /* Handle class loader classes 处理类加载器*/

   if(cb->name == SYMBOL(java_lang_ClassLoader)) {
       classlibCacheClassLoaderFields(class);
       cb->flags |= CLASS_LOADER;
   }

   cb->state = CLASS_LINKED;

unlock:
   objectUnlock(class);
}

/*
    初始化class
    JVM那本书是这么描述初始化的：
        初始化阶段就是执行类构造器<clinit >()方法的过程。

        <clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的 语句合并产生的，
        编译器收集的顺序是由语句在源文件中出现的顺序决定的，静态语句块中只能访问到定义在静态语句块之前的变量，
        定义在它之后的变量，在前面的静态语句块可以赋值，但是不能访问

        <clinit>()方法与类的构造函数(即在虚拟机视角中的实例构造器<init>()方法)不同，
        它不需要显式地调用父类构造器，Java虚拟机会保证在子类的<clinit >()方法执行前，父类的<clinit >()方法已经执行 完毕。

    这个方法里面做的事情也是差不多
        先加锁，避免被多次执行
        查看是否有父类，有的话先初始化父类
        将static类型的字段进行赋值
            这里有一些特殊的情况，如果改字段是一个Object类型的字段，那么会解析这个Class的限定名称为一个指针，查找这个Class的时候，只会判断这个Class是否Link过，而不会初始化这个Class（init传的false）
*/
Class *initClass(Class *class) {
   ClassBlock *cb = CLASS_CB(class);
   ConstantPool *cp = &cb->constant_pool;
   FieldBlock *fb = cb->fields;
   MethodBlock *mb;
   Object *excep;
   int state, i;
   
   // 如果已经初始化过 则跳过
   if(cb->state >= CLASS_INITED)
      return class;
   // 链接一下 如果已经链接过则会直接跳过
   linkClass(class);

   /* 
        Java虚拟机必须保证一个类的<clinit >()方法在多线程环境中被正确地加锁同步，
        如果多个线程同 时去初始化一个类，那么只会有其中一个线程去执行这个类的<clinit>()方法，
        其他线程都需要阻塞等 待，直到活动线程执行完毕<clinit >()方法。 */
   // 加锁
   objectLock(class);
   
   while(cb->state == CLASS_INITING)
      if(cb->initing_tid == threadSelf()->id) {
         objectUnlock(class);
         return class;
      } else {
          /* FALSE means this wait is non-interruptible.
             An interrupt will appear as if the initialiser
             failed (below), and clearing will lose the
             interrupt status 

             FALSE表示此等待是不可中断的。如果初始化失败(如下所示)，中断将出现，并且清除将丢失中断状态*/
          objectWait(class, 0, 0, FALSE);
      }

   if(cb->state >= CLASS_INITED) {
      objectUnlock(class);
      return class;
   }
   
   // 初始化失败了
   if(cb->state == CLASS_BAD) {
       objectUnlock(class);
       signalException(java_lang_NoClassDefFoundError, cb->name);
       return NULL;
   }

   cb->state = CLASS_INITING;
   cb->initing_tid = threadSelf()->id;

   objectUnlock(class);

   /* 也就是说，如果是一个接口的话，是可以不用先执行父接口的<cinit>方法的，接口的实现类在初始化时也一样不会执行接口的<clinit >()方法。只会执行父类的<clinit>方法*/
   if(!(cb->access_flags & ACC_INTERFACE)  // 不是一个接口
              && cb->super // 有父类
              && (CLASS_CB(cb->super)->state != CLASS_INITED)) { // 且父类未初始化
      initClass(cb->super); // 则先初始化父类
      if(exceptionOccurred()) {  // 在这个过程当中失败了 则跳转 解锁、唤起其他线程、设置Class加载失败
          state = CLASS_BAD;
          goto set_state_and_notify;
      }
   }

   /* Never used to bother with this as only static finals use it and
      the constant value's copied at compile time.  However, separate
      compilation can result in a getstatic to a (now) constant field,
      and the VM didn't initialise it... 

      因为只有静态final才会使用它，常量值在编译时才会被复制。
      然而，单独编译可能会导致getstatic到(now)常量字段，而且VM没有初始化它……
      */

   for(i = 0; i < cb->fields_count; i++,fb++)
      if((fb->access_flags & ACC_STATIC) && fb->constant) {
         if((*fb->type == 'J') || (*fb->type == 'D')) // J long, D double
            fb->u.static_value.l = *(u8*)&(CP_INFO(cp, fb->constant)); // 这里就是把类似于 public static long a = 1000L; 的1000L 赋值给a
         else
            fb->u.static_value.u = resolveSingleConstant(class, fb->constant);  // 从常量池中解析一个常量 可以是字符串、可以是对象
      }

   // 查找<clinit>这个方法
   if((mb = findMethod(class, SYMBOL(class_init), SYMBOL(___V))) != NULL)
      // 执行这个<clinit>方法 居然还是个静态的 啧啧啧
      executeStaticMethod(class, mb);

   if((excep = exceptionOccurred())) {  // 出现异常了
       Class *error;

       clearException(); 

       /* Don't wrap exceptions of type java.lang.Error... 不要将java.lang.Error...类型的异常包起来。*/
       error = findSystemClass0(SYMBOL(java_lang_Error));
       if(error != NULL && !isInstanceOf(error, excep->class)) {
           Class *init_error = findSystemClass(
                         SYMBOL(java_lang_ExceptionInInitializerError));
           if(init_error != NULL) {
               mb = findMethod(init_error, SYMBOL(object_init),
                                           SYMBOL(_java_lang_Throwable__V));
               if(mb != NULL) {
                   Object *ob = allocObject(init_error);

                   if(ob != NULL) {
                       executeMethod(ob, mb, excep);  // 执行ExceptionInInitializerError的<init>方法
                       excep = ob;
                   }
               }
           }
       }

       setException(excep);

       state = CLASS_BAD;
   } else
       state = CLASS_INITED;  // 初始化成功~
   
set_state_and_notify:
   objectLock(class);
   cb->state = state;

   objectNotifyAll(class);
   objectUnlock(class);

   return state == CLASS_BAD ? NULL : class;
}

char *findFileEntry(char *path, int *file_len) {
    int read_len;
    char *data;
    FILE *fd;

    if((fd = fopen(path, "r")) == NULL)
        return NULL;

    fseek(fd, 0L, SEEK_END);
    *file_len = ftell(fd);
    fseek(fd, 0L, SEEK_SET);

    data = sysMalloc(*file_len);
    read_len = fread(data, sizeof(char), *file_len, fd);
    fclose(fd);

    if(read_len == *file_len)
        return data;

    sysFree(data);
    return NULL;
}

void defineBootPackage(char *classname, int index) {
    char *last_slash = strrchr(classname, '/');

    if(last_slash != NULL && last_slash != classname) {
        int len = last_slash - classname + 1;
        PackageEntry *package = sysMalloc(sizeof(PackageEntry) + len);
        PackageEntry *hashed;
        
        package->index = index;
        slash2DotsBuff(classname, package->name, len);

#undef HASH
#undef COMPARE
#define HASH(ptr) utf8Hash(((PackageEntry*)ptr)->name)
#define COMPARE(ptr1, ptr2, hash1, hash2) (hash1 == hash2 && \
            utf8Comp(((PackageEntry*)ptr1)->name, ((PackageEntry*)ptr2)->name))

        /* Add if absent, no scavenge, locked */
        findHashEntry(boot_packages, package, hashed, TRUE, FALSE, TRUE);

        if(package != hashed)
            sysFree(package);
    }
}

/*
    在加载阶段，Java虚拟机需要完成以下三件事情:
    1)通过一个类的全限定名来获取定义此类的二进制字节流。
        Java虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节 流”这个动作放到Java虚拟机外部去实现，
        以便让应用程序自己决定如何去获取所需的类。
        实现这个动 作的代码被称为“类加载器”(Class Loader)。
    2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
    3)在内存中生成一个代表这个类的java.lang.Class对象，作为方法区这个类的各种数据的访问入口。

    上面这个说法，是作为一个普通类的加载方式的过程。一般体现在Java的类加载器当中。
    作为下面这个方法，是加载系统的Class，是JVM层面的，可能有所不一样。
*/
Class *loadSystemClass(char *classname) {
    int file_len, fname_len = strlen(classname) + 8;
    char buff[max_cp_element_len + fname_len]; // 诶 max_cp_element_len这个被用到了 这个之前是存储的非文件的（目录）字符串的最长长度
    char filename[fname_len];
    Class *class = NULL;
    char *data = NULL;
    int i;

    filename[0] = '/';
    strcat(strcpy(&filename[1], classname), ".class"); // 拼接成/xxxx/sasadf/xxx.class

   // 1)通过一个类的全限定名来获取定义此类的二进制字节流。
   // 这里是从ZIP压缩包中读取
   /*
        除了zip压缩包获取，还可以通过：
        从网络中获取，这种场景最典型的应用就是Web Applet。
        运行时计算生成，这种场景使用得最多的就是动态代理技术，在java.lang.reflect .Proxy 中，就是用 了Proxy Generator.generateProxy Class()来为特定接口生成形式为“*$Proxy ”的代理类的二进制字节流。
        由其他文件生成，典型场景是JSP应用，由JSP文件生成对应的Class文件。
        从数据库中读取，这种场景相对少见些，例如有些中间件服务器(如SAP Netweaver)可以选择把程序安装到数据库中来完成程序代码在集群间的分发。
        可以从加密文件中获取，这是典型的防Class文件被反编译的保护措施，通过加载时解密Class文 件来保障程序运行逻辑不被窥探。
        
   */
    for(i = 0; i < bcp_entries && data == NULL; i++) // 我知道了，这一步是从 bootclasspath的zip文件中 获取classname对应的类的数据！！！ bootclasspath 就是前面的initialiseClassStage1()方法中的parseBootClassPath()方法处理的
        if(bootclasspath[i].zip)
            data = findArchiveEntry(filename + 1, bootclasspath[i].zip,
                                    &file_len);
        else
            data = findFileEntry(strcat(strcpy(buff, bootclasspath[i].path),
                                 filename), &file_len);

    if(data == NULL) {
        signalException(java_lang_NoClassDefFoundError, classname);
        return NULL;
    }

    /* If this class belongs to a package not yet seen add it
       to the list of bootloader packages 

       如果这个类属于一个尚未看到的包，请将其添加到引导加载程序包列表中 */
    defineBootPackage(classname, i - 1); // TODO 所以这个方法是把classname给加入到boot_packages这个hash表中，记录为系统class
    // 2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
    class = defineClass(classname, data, 0, file_len, NULL); 
    sysFree(data);

    if(verbose && class)
        jam_printf("[Loaded %s from %s]\n", classname, bootclasspath[i-1].path);

    return class;
}

void addInitiatingLoaderToClass(Object *class_loader, Class *class) {
    ClassBlock *cb = CLASS_CB(class);

    /* The defining class loader is automatically an initiating
       loader so don't add again */
    if(cb->class_loader != class_loader)
        addClassToHash(class, class_loader);
}

/**
 * 获取哈希处理过的class信息
 * 
 * 这个方法不怎么看得懂。。
 **/
Class *findHashedClass(char *classname, Object *class_loader) {
    HashTable *table;
    Class *class;
    char *name;

    /* If the class name is not in the utf8 table it can't
       have been loaded 
       如果类名不在utf8表中，它就不能被加载 */
    if((name = findUtf8(classname)) == NULL)
        return NULL;

    if(class_loader == NULL)
        table = &boot_classes;
    else
        if((table = classlibLoaderTable(class_loader)) == NULL)
            return NULL;

#undef HASH
#undef COMPARE
#define HASH(ptr) utf8Hash(ptr)
#define COMPARE(ptr1, ptr2, hash1, hash2) (hash1 == hash2) && \
            (ptr1 == CLASS_CB((Class *)ptr2)->name)

    /* Do not add if absent, no scavenge, locked 
    如果不在，没有搜刮，锁定，请不要添加。
    不添加，如果缺席，没有清除，锁定(翻译好难)*/
   findHashEntry((*table), name, class, FALSE, FALSE, TRUE);

   return class;
}

/**
 * 获取系统的class
 * 查找、载入、链接
 * Class的生命周期分为：
 *  加载、链接（验证、准备、解析）、初始化、使用、卸载
 **/
Class *findSystemClass0(char *classname) {
   // 先查找 这一步 会尝试从一张hash表中查询classname对应的class 如果不存在的话返回null 但这个classname会被存入hash表中
   Class *class = findHashedClass(classname, NULL);

   if(class == NULL)
       // TODO 然后载入
       class = loadSystemClass(classname);

   if(!exceptionOccurred())
       // TODO 然后链接 里面的代码贼长 值得一看！！！！！！
       linkClass(class); 
   // 我的天哪，这不就是虚拟机规范里面说的吗 终于看到源码了
   return class;
}

/*
    在findSystemClass0的基础上 初始化了class
    查找、载入、链接、初始化
*/
Class *findSystemClass(char *classname) {
   Class *class = findSystemClass0(classname);

   if(!exceptionOccurred())
       initClass(class);

   return class;
}

// 不是的！！不是多个类，而是类的array类型！！！比如Object[] String[] Class[] 等等！！！
/* 
    对于代码 Object[] objects = new Object[10]; 来说
    会生成一个名为[java.long.Object的类
    而这个类的初始化方式，就是下面这句代码
    */
Class *findArrayClassFromClassLoader(char *classname, Object *class_loader) {
   Class *class = findHashedClass(classname, class_loader);

   if(class == NULL)
       if((class = createArrayClass(classname, class_loader)) != NULL)
           addInitiatingLoaderToClass(class_loader, class);

   return class;
}

Class *findPrimitiveClassByName(char *classname) {
   int index;
   Class *prim;
   char *prim_name;

    if((prim_name = findUtf8(classname)) == NULL)
        goto error;

    if(prim_name == SYMBOL(boolean))
       index = PRIM_IDX_BOOLEAN;
    else if(prim_name == SYMBOL(byte))
       index = PRIM_IDX_BYTE;
    else if(prim_name == SYMBOL(char))
       index = PRIM_IDX_CHAR;
    else if(prim_name == SYMBOL(short))
        index = PRIM_IDX_SHORT;
    else if(prim_name == SYMBOL(int))
        index = PRIM_IDX_INT;
    else if(prim_name == SYMBOL(float))
        index = PRIM_IDX_FLOAT;
    else if(prim_name == SYMBOL(long))
        index = PRIM_IDX_LONG;
    else if(prim_name == SYMBOL(double))
        index = PRIM_IDX_DOUBLE;
    else if(prim_name == SYMBOL(void))
        index = PRIM_IDX_VOID;
    else
        goto error;

    prim = prim_classes[index];
    return prim ? prim : createPrimClass(prim_name, index);

error:
    signalException(java_lang_NoClassDefFoundError, NULL);
    return NULL;
}

Class *findPrimitiveClass(char prim_type) {
   int index;
   Class *prim;
   char *classname;

   switch(prim_type) {
      case 'Z':
          classname = SYMBOL(boolean);
          index = PRIM_IDX_BOOLEAN;
          break;
      case 'B':
          classname = SYMBOL(byte);
          index = PRIM_IDX_BYTE;
          break;
      case 'C':
          classname = SYMBOL(char);
          index = PRIM_IDX_CHAR;
          break;
      case 'S':
          classname = SYMBOL(short);
           index = PRIM_IDX_SHORT;
          break;
      case 'I':
          classname = SYMBOL(int);
           index = PRIM_IDX_INT;
          break;
      case 'F':
          classname = SYMBOL(float);
           index = PRIM_IDX_FLOAT;
          break;
      case 'J':
          classname = SYMBOL(long);
           index = PRIM_IDX_LONG;
          break;
      case 'D':
          classname = SYMBOL(double);
           index = PRIM_IDX_DOUBLE;
          break;
      case 'V':
          classname = SYMBOL(void);
           index = PRIM_IDX_VOID;
          break;
      default:
          signalException(java_lang_NoClassDefFoundError, NULL);
          return NULL;
   }

   prim = prim_classes[index];
   return prim ? prim : createPrimClass(classname, index);
}

// TODO 这个方法是类加载器加载类的  具体做了啥捏
Class *findNonArrayClassFromClassLoader(char *classname, Object *loader) {
    Class *class = findHashedClass(classname, loader);

    if(class == NULL) {
        char *dot_name = slash2DotsDup(classname);
        Object *string = createString(dot_name);
        MethodBlock *loadClass;
        Object *excep;

        sysFree(dot_name);
        if(string == NULL)
            return NULL;

        if(loadClass_mtbl_idx == -1) {
            MethodBlock *mb = lookupMethod(loader->class, SYMBOL(loadClass),
                            SYMBOL(_java_lang_String__java_lang_Class));
            if(mb == NULL)
                return NULL;

            loadClass_mtbl_idx = mb->method_table_index;
        }

        loadClass = CLASS_CB(loader->class)->method_table[loadClass_mtbl_idx];

        /* The public loadClass is not synchronized.
           Lock the class-loader to be thread-safe */
        objectLock(loader);
        class = *(Class**)executeMethod(loader, loadClass, string);
        objectUnlock(loader);

        if((excep = exceptionOccurred()) || class == NULL) {
            clearException();
            signalChainedException(java_lang_NoClassDefFoundError,
                                   classname, excep);
            return NULL;
        }

        addInitiatingLoaderToClass(loader, class);

        if(verbose && (CLASS_CB(class)->class_loader == loader))
            jam_printf("[Loaded %s]\n", classname);
    }
    return class;
}

Class *findClassFromClassLoader(char *classname, Object *loader) {
    // 看名字是跳过反射的加载？？？
    // TODO 啥意思
    loader = classlibSkipReflectionLoader(loader);

    // 对于代码 Object[] objects = new Object[10];  会生成一个名为[java.long.Object的类 就是[开头的了
    if(*classname == '[')
        return findArrayClassFromClassLoader(classname, loader);

    // 如果加载器不为空就从类加载器加载不是Array的类
    if(loader != NULL)
        return findNonArrayClassFromClassLoader(classname, loader);

    // 加载系统的类
    return findSystemClass0(classname);
}

/*
    获取系统的类加载器
*/
Object *getSystemClassLoader() {
    // 老操作 获取class 查找、载入、链接、初始化
    Class *class_loader = findSystemClass(SYMBOL(java_lang_ClassLoader));

    if(!exceptionOccurred()) {
        // 没有异常就获取方法 根据方法名称和返回类型获取
        MethodBlock *mb = findMethod(class_loader,
                                     SYMBOL(getSystemClassLoader),
                                     SYMBOL(___java_lang_ClassLoader));

        if(mb != NULL) {
            // 执行ClassLoader.getSystemClassLoader()方法 获取一个classLoder
            Object *loader = *(Object**)executeStaticMethod(class_loader, mb);

            if(!exceptionOccurred()) 
                return loader;
        }
    }
    return NULL;
}

Object *bootPackage(char *package_name) {
    PackageEntry *hashed;

#undef HASH
#undef COMPARE
#define HASH(ptr) utf8Hash(ptr)
#define COMPARE(ptr1, ptr2, hash1, hash2) (hash1 == hash2 && \
                                 utf8Comp(ptr1, ((PackageEntry*)ptr2)->name))

    /* Do not add if absent, no scavenge, locked */
    findHashEntry(boot_packages, package_name, hashed, FALSE, FALSE, TRUE);

    if(hashed != NULL)
        return classlibBootPackage(hashed);

    return NULL;
}

#define ITERATE(ptr)                                          \
    if((data[--count] = classlibBootPackages(ptr)) == NULL) { \
        array = NULL;                                         \
        goto error;                                           \
    }

Object *bootPackages() {
    Class *array_class = classlibBootPackagesArrayClass();
    Object **data, *array;
    int count;

    lockHashTable(boot_packages);

    count = hashTableCount(boot_packages);
    if((array = allocArray(array_class, count, sizeof(Object*))) == NULL)
        goto error;

    data = ARRAY_DATA(array, Object*);
    hashIterate(boot_packages);

error:
    unlockHashTable(boot_packages);
    return array;
}

/* gc support for marking classes */

#undef ITERATE
#define ITERATE(ptr) markRoot(ptr)

void markBootClasses() {
   int i;

   hashIterate(boot_classes);  // 这里迭代了一下hash表？

   for(i = 0; i < MAX_PRIM_CLASSES; i++)
       if(prim_classes[i] != NULL)
           markRoot((Object*)prim_classes[i]); // TODO 然后这里把prim_classes里面的对象增加了个标记头？用处何在？ 这个跟上面那个关联在哪？
}

#undef ITERATE
#define ITERATE(ptr)  threadReference((Object**)ptr)

void threadBootClasses() {
   int i;

   hashIterateP(boot_classes);

   for(i = 0; i < MAX_PRIM_CLASSES; i++)
       if(prim_classes[i] != NULL)
           threadReference((Object**)&prim_classes[i]);
}

#undef ITERATE
#define ITERATE(ptr)                                         \
    if(CLASS_CB((Class *)ptr)->class_loader == class_loader) \
        markObject(ptr, mark)

void markLoaderClasses(Object *class_loader, int mark) {
    HashTable *table = classlibLoaderTable(class_loader);

    if(table != NULL) {
        hashIterate((*table));
    }
}

#undef ITERATE
#define ITERATE(ptr) threadReference((Object**)ptr)

void threadLoaderClasses(Object *class_loader) {
    HashTable *table = classlibLoaderTable(class_loader);

    if(table != NULL) {
        hashIterateP((*table));
    }
}

static void freeIndexedAttributes(AttributeData **attributes, int size) {
    int i;

    if(attributes == NULL)
        return;

    for(i = 0; i < size; i++)
        if(attributes[i] != NULL) {
            gcPendingFree(attributes[i]->data);
            gcPendingFree(attributes[i]);
        }

    gcPendingFree(attributes);
}

#define freeSingleAttributes(attributes) \
    if(attributes != NULL) {             \
        gcPendingFree(attributes->data); \
        gcPendingFree(attributes);       \
    }

void freeClassData(Class *class) {
    ClassBlock *cb = CLASS_CB(class);
    int i;

    if(IS_ARRAY(cb)) {
        gcPendingFree(cb->interfaces);
        return;
    }

#ifdef JSR292
    freeResolvedPolyData(class);
#endif

    gcPendingFree((void*)cb->constant_pool.type);
    gcPendingFree(cb->constant_pool.info);
    gcPendingFree(cb->interfaces);
    gcPendingFree(cb->fields);

    for(i = 0; i < cb->methods_count; i++) {
        MethodBlock *mb = &cb->methods[i];

#ifdef DIRECT
        if(mb->state == MB_PREPARED) {
#ifdef INLINING
            freeMethodInlinedInfo(mb);
#endif
            gcPendingFree(mb->code);
        } else
#endif
        if(!(mb->access_flags & (ACC_NATIVE | ACC_ABSTRACT | ACC_MIRANDA)))
            gcPendingFree(mb->code);

        /* Miranda methods have no exception, line number
           or throw tables */
        if(mb->access_flags & ACC_MIRANDA)
            continue;

        if(!(mb->access_flags & ACC_NATIVE)) {
            gcPendingFree(mb->exception_table);
            gcPendingFree(mb->line_no_table);
        }
        gcPendingFree(mb->throw_table);
    } 

    if(cb->extra_attributes != NULL) {
        int methods_count;

        /* The method extra attributes does not include the Miranda
           methods so we need to adjust the methods count.  The
           Mirandas are all at the end of the methods and are small
           in number - this quick loop is preferable to storing an
           extra count in the method block.  Likewise, we could put
           something in the loop above to track the last non-Miranda,
           but extra attributes are also rare. */
        for(i = cb->methods_count - 1; i >= 0 &&
            cb->methods[i].access_flags & ACC_MIRANDA; i--);
        methods_count = i + 1;

        freeSingleAttributes(cb->extra_attributes->class_annos);

        freeIndexedAttributes(cb->extra_attributes->field_annos,
                              cb->fields_count);
        freeIndexedAttributes(cb->extra_attributes->method_annos,
                              methods_count);
        freeIndexedAttributes(cb->extra_attributes->method_parameter_annos,
                              methods_count);
        freeIndexedAttributes(cb->extra_attributes->method_anno_default_val,
                              methods_count);

#ifdef JSR308
        freeSingleAttributes(cb->extra_attributes->class_type_annos);
        freeIndexedAttributes(cb->extra_attributes->field_type_annos,
                              cb->fields_count);
        freeIndexedAttributes(cb->extra_attributes->method_type_annos,
                              methods_count);
#endif
#ifdef JSR901
        freeIndexedAttributes(cb->extra_attributes->method_parameters,
                              methods_count);
#endif

        gcPendingFree(cb->extra_attributes);
    }

    gcPendingFree(cb->methods);
    gcPendingFree(cb->inner_classes);

    if(cb->state >= CLASS_LINKED) {
        ClassBlock *super_cb = CLASS_CB(cb->super);

        /* interfaces do not have a method table, or 
            imethod table offsets */
        if(!IS_INTERFACE(cb)) {
            int spr_imthd_sze = super_cb->imethod_table_size;

            gcPendingFree(cb->method_table);
            if(cb->imethod_table_size > spr_imthd_sze)
                gcPendingFree(cb->imethod_table[spr_imthd_sze].offsets);
        }

        gcPendingFree(cb->imethod_table);

        if(cb->refs_offsets_table != super_cb->refs_offsets_table)
            gcPendingFree(cb->refs_offsets_table);
    }
}

void freeClassLoaderData(Object *class_loader) {
    HashTable *table = classlibLoaderTable(class_loader);

    if(table != NULL) {
        gcFreeHashTable((*table));
        gcPendingFree(table);
    }
}

void parseBootClassPath() {
    char *cp, *pntr, *start;
    int i, j, len, max = 0;
    struct stat info;

    cp = sysMalloc(strlen(bootpath)+1);//为啥每次要加1
    strcpy(cp, bootpath);
    // 这段代码达成的结果是是 i的数量为:分割之后的数量
    for(i = 0, start = pntr = cp; *pntr; pntr++) {
        if(*pntr == ':') {
            if(start != pntr) {
                *pntr = '\0';
                i++;
            }
            start = pntr+1;
        }
    }
    if(start != pntr)
        i++;

    bootclasspath = sysMalloc(sizeof(BCPEntry)*i); // BCPEntry: 保存引导加载程序类路径的结构 这里应该是初始化i个BCPEntry的内存
    // 下面这段代码 其实就是把bootpath中的文件打包了 放到了bootclasspath中
    for(j = 0, pntr = cp; i > 0; i--) {
        while(*pntr == ':')
            pntr++;

        start = pntr;
        pntr += (len = strlen(pntr))+1;

        if(stat(start, &info) == 0) { // stat 获取文件属性的文件，并把它们放在BUF。
            if(S_ISDIR(info.st_mode)) { // 是否文件夹
                bootclasspath[j].zip = NULL; //文件夹就没有zip包了
                if(len > max)
                    max = len;
            } else
                if((bootclasspath[j].zip = processArchive(start)) == NULL) // 这个是打包吗
                    continue;
            bootclasspath[j++].path = start;
        }
    }

    max_cp_element_len = max; // 这个是干嘛用的 看名字说的是最大的copy元素的长度 上面max的赋值只有一个地方max = len 而len是字符串的长度 所以这个是最长的字符串长度？ 但这个字符串长度也不是一定的，得是字符串是目录的时候才会赋值
    bcp_entries = j;  // 就是bootclasspath的实际个数
}

void setClassPath(InitArgs *args) {
    char *env;
    classpath = args->classpath ? args->classpath : 
                 ((env = getenv("CLASSPATH")) ? env : "."); // ".:/usr/local/jdk1.8.0_11/lib/dt.jar:/usr/local/jdk1.8.0_11/lib/tools.jar"
}

char *getClassPath() {
    return classpath;
}

#ifdef __linux__
int filter(const struct dirent *entry) {
#else
int filter(struct dirent *entry) {
#endif
    int len = strlen(entry->d_name);
    char *ext = (char*)&entry->d_name[len-4];

    return len >= 4 && (strcasecmp(ext, ".zip") == 0 ||
                        strcasecmp(ext, ".jar") == 0);
}
// TODO 看起来好像是把dir这个目录下的jar包给添加到bootpath中
void scanDirForJars(char *dir) {
    int bootpathlen = strlen(bootpath) + 1;
    int dirlen = strlen(dir);
    struct dirent **namelist;
    int n;

    n = scandir(dir, &namelist, &filter, &alphasort);

    if(n >= 0) {
        while(--n >= 0) {
            char *buff;
            bootpathlen += strlen(namelist[n]->d_name) + dirlen + 2;
            buff = sysMalloc(bootpathlen);

            strcat(strcat(strcat(strcat(strcpy(buff, dir), "/"),
                                 namelist[n]->d_name), ":"), bootpath);

            sysFree(bootpath);
            bootpath = buff;
            free(namelist[n]);
        }
        free(namelist);
    }
}

void scanDirsForJars(char *directories) {
    int dirslen = strlen(directories); // TODO 这个方法没有调试到 下次进来需要调试一下
    char *pntr, *end, *dirs = sysMalloc(dirslen + 1);

    strcpy(dirs, directories);
    for(end = pntr = &dirs[dirslen]; pntr != dirs; pntr--) {
        if(*pntr == ':') {
            char *start = pntr + 1;
            if(start != end)
                scanDirForJars(start); // 这个看起来像是到某个目录下搜索jar包

            *(end = pntr) = '\0';
        }
    }

    if(end != dirs)
        scanDirForJars(dirs);

    sysFree(dirs);
}

char *getEndorsedDirs() {
    char *endorsed_dirs = getCommandLineProperty("java.endorsed.dirs"); // 优先从命令行拿
    if(endorsed_dirs == NULL)
        endorsed_dirs = classlibDefaultEndorsedDirs(); // 否则从gnu 或者jdk拿

    return endorsed_dirs; // "/usr/local/jamvm/share/jamvm/endorsed"
}

void setBootClassPath(InitArgs *args) {
    char *path = args->bootpath;

    if(path == NULL)
        path = getCommandLineProperty("sun.boot.class.path");
    if(path == NULL)
        path = getCommandLineProperty("java.boot.class.path");
    if(path == NULL)
        path = getenv("BOOTCLASSPATH");
    if(path == NULL)
        path = classlibBootClassPathOpt(args); // 我当前环境 调用了gnu classpath的方法 执行完之后: "/usr/local/jamvm/share/jamvm/classes.zip:/usr/local/classpath/share/classpath/glibj.zip"

    if(args->bootpath_a != NULL) {
        bootpath = sysMalloc(strlen(path) + strlen(args->bootpath_a) + 2);
        strcat(strcat(strcpy(bootpath, path), ":"), args->bootpath_a); // 这里应该是在path的结尾 追加了命令行参数中的bootpath_a的配置信息
    } else
        bootpath = strcpy(sysMalloc(strlen(path) + 1), path); // 如果没有设置bootpath_a的话，默认就是path了 这里给bootpath分配内存
    // TODO scanDirsForJars这个方法没有调试到 下次进来需要调试一下
    scanDirsForJars(getEndorsedDirs()); // getEndorsedDirs: 得到认可Dirs 返回 "/usr/local/jamvm/share/jamvm/endorsed"

    if(args->bootpath_p != NULL) {
        path = sysMalloc(strlen(bootpath) + strlen(args->bootpath_p) + 2);
        strcat(strcat(strcpy(path, args->bootpath_p), ":"), bootpath);
        sysFree(bootpath);
        bootpath = path;    // 这里其实就是从启动参数的bootpath_p中获取参数 然后分配内存，拼接上面的字符串 赋值给 bootpath
    } // bootpath的值: "/usr/local/jamvm/share/jamvm/classes.zip:/usr/local/classpath/share/classpath/glibj.zip"
}

char *getBootClassPath() {
    return bootpath;
}

char *getBootClassPathEntry(int index) {
    return bootclasspath[index].path;
}

int bootClassPathSize() {
    return bcp_entries;
}

Object *bootClassPathResource(char *filename, int index) {
    Object *res = NULL;

    if(index < bcp_entries) {
        char *buff, *path = bootclasspath[index].path;
        int path_len = strlen(path);

        if(path[0] != '/') {
            char *cwd = getCwd();
            path_len += strlen(cwd) + 1;
            path = strcat(strcat(strcpy(sysMalloc(path_len + 1), cwd),
                                 "/"), path);
            sysFree(cwd);
        }

        /* Alloc enough space for Jar file URL --
           jar:file://<path>!/<filename> */
        buff = sysMalloc(strlen(filename) + path_len + 14);

        if(bootclasspath[index].zip != NULL) {
            while(*filename == '/')
                filename++;

            if(findArchiveDirEntry(filename, bootclasspath[index].zip) == NULL)
                goto out;

            sprintf(buff, "jar:file://%s!/%s", path, filename);
        } else {
            struct stat info;

            sprintf(buff, "file://%s/%s", path, filename);
            if(stat(&buff[7], &info) != 0 || S_ISDIR(info.st_mode))
                goto out;
        }

        res = createString(buff);

out:
        if(path != bootclasspath[index].path)
            sysFree(path);
        sysFree(buff);
    }

    return res;
}

int initialiseClassStage1(InitArgs *args) {
    // 这个应该就是设置是否打印那些日志的一个开关
    verbose = args->verboseclass;

    // 看名字就知道 内部优先从args.classpath获取 如果不存在就从env获取 应该就是系统的缓解变量
    setClassPath(args);
    // 看名字就知道 不过boot classpath是干嘛用的 里面用到了启动参数 bootpath_p bootpath_a等参数
    setBootClassPath(args);
    // 看名字就知道 解析boot classpath 里面把上一步解析到的路径，进行文件打包了，（虽然我不知道那个方法是不是打包）然后赋值到了bootclasspath这个解构中
    parseBootClassPath();

    /* Init hash table, and create lock for the bootclassloader classes 
        初始化哈希表，并为bootclassloader类创建锁 大小为 1<<8 = 256*/
    initHashTable(boot_classes, CLASS_INITSZE, TRUE); // TODO 这个表是用来干嘛的呀

    /* Init hash table, and create lock for the bootclassloader packages 
        初始化哈希表，并为bootclassloader包创建锁 1<<6 = 64*/
    initHashTable(boot_packages, PCKG_INITSZE, TRUE);// TODO 这个表是用来干嘛的呀

    /* Register the address of where the java.lang.Class ref _will_ be 
        注册java.lang.Class ref _will_所在的地址 */
    //  看了下里面的代码 就是注册class 把class给注册到了一个数组中，好像是会被gc扫描到的
    // TODO 用处何在
    registerStaticClassRef(&java_lang_Class);

    /* Do classlib specific class initialisation 做classlib特定类的初始化 */
    if(!classlibInitialiseClass()) { // 我这里是gnu classpath的相关代码
        jam_fprintf(stderr, "Error initialising VM (initialiseClassStage1)\n");
        return FALSE;
    }

    return TRUE;
}

int initialiseClassStage2() {
    int padding = offsetof(ClassBlock, state);
    int fields = CLASS_CB(java_lang_Class)->object_size - sizeof(Object);
    
    // 大概看出来了 就是判断ClassBlock的填充大小不能小于class的字段
    // TODO 意义是啥，padding 填充又是啥？
    if(padding < fields) {
        jam_fprintf(stderr, "Error initialising VM (initialiseClassStage2)\n"
                            "ClassBlock padding is less than java.lang.Class "
                            "fields!\n");
        return FALSE;
    }

    /* Ensure that java.lang.Class is initialised.  We can't do it in
       stage1 (above) as it is too early in the initialisation process
       to run Java code 
       确保java.lang.Class被初始化。
       我们不能在阶段1(上面)中这样做，因为在初始化过程中运行Java代码还太早
       */
    if(initClass(java_lang_Class) == NULL) {
        jam_fprintf(stderr, "Error initialising VM (initialiseClassStage2)\n"
                            "java.lang.Class could not be initialised!\n");
        return FALSE;
    }

    return TRUE;
}
